鄰面合併(代碼解讀+照貓畫虎)

原文鏈接:https://blog.csdn.net/g21glf

描述

【題目背景】

NEWorld作爲一個3D遊戲,對渲染(圖形繪製)的效率要求極高。當玩家擴大視野範圍時,可見的方塊面數量將會迅速增多,以至於大量的頂點處理很快就成爲了圖形管線中的瓶頸。喬貓想了想,決定在大量繪製前,預處理一些相鄰且有着相同材質的方塊面——將許多小的面合成一個大的面,便可以在不改變渲染結果的同時減少很多頂點數量了吧……

【問題描述】

給定一個NMN∗ M的網格,每個格子上寫有010或1。現在用一些長方形覆蓋其中寫有11的格子,長方形的每條邊都要與座標軸平行。要求:每個寫着11的格子都要被覆蓋,長方形不可以重疊(重複繪製也多少會增加性能開銷),也不能覆蓋到任何一個寫着00的格子(不然繪製結果就不正確了)。請問最少需要多少長方形?

輸入

輸入文件第一行兩個正整數N,MN,M,表示網格大小爲NMN行M列

接下來的NN行,每行MM個正整數Ai,j01Ai,j(保證均爲0或1),其中ij第i行j列的正整數表示網格iji行j列裏填的數。

輸出

輸出文件包含一行一個正整數,表示最少需要的長方形數量。

樣例輸入

4 4
1 1 1 0
1 1 1 1
0 0 1 1
0 0 1 1

樣例輸出

3

提示

對於 3030%的數據N,M<=5N,M<=5
對於 100100%的數據 n<=100,M<=8n<=100,M<=8

解析:

看到MM的範圍很小,就要有條件反射:狀壓DP
然後,就開始DP吧······
GLF大佬博客

#include<bits/stdc++.h>//代碼由GLF大佬提供
using namespace std;
const int MAXN=110;
const int MAXM=1<<9;
const int INF=0x3f3f3f3f;
int n,m;
int ma[MAXN][10];
int dp[MAXN][MAXM];//state:當前點作爲一個矩陣左端點 
int Read(){
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}
int check(int x,int state){//判斷當前設想的狀態是否合法 
	for(int i=1;i<=m;++i){
		if((state&(1<<(i-1)))&&!ma[x][i])      //當前點沒有但作爲矩陣點,則不合法 
		  return 0;
	}
	int flag=0;
	for(int i=1;i<=m;++i){//枚舉這一行的所有點 
		if(state&(1<<(i-1)))  flag=1;          //當前點作爲矩陣點,枚舉每個點時都要判斷一次 
		if(ma[x][i]&&!flag)  return 0;         //當前點有但沒在矩陣中 ,即設想狀態不合法 
		if(!ma[x][i])  flag=0;//當前點沒有 
	}//注:狀態二進制中的"0"不一定代表當前位置沒有點,也有可能繼承上一個"1"位置的信息,及當前"0"位置可能有點(視具體情況分析),狀態中,每一個"1"都代表一個新矩陣的開始 
	return 1;
}
int calc(int x,int state,int pre){             //x:當前層 state:當前層狀態 pre:上一層狀態 
	int ret=0;
	for(int i=1;i<=m;++i){
		if(state&(1<<(i-1)))  ++ret;           //最壞情況所有點單獨作爲一個矩陣 (ret計算state中有多少個"1") 
	}
	for(int i=1;i<=m;++i){
		if((state&(1<<(i-1)))&&(pre&(1<<(i-1)))){   //當前位置上一層有點考慮合併 
			int up=i;
			while(ma[x][up]&&up<=m+1){              //找連續1,up的上限可改爲m ///////////////////////////////////////////////
				if(up!=i&&(state&(1<<(up-1))))      //當前點在下一個矩陣中 
					break;
				++up;
			}//up記錄從當前矩陣開始到下一個矩陣開始的長度差 
			int flag=0;
			for(int j=i+1;j<up;++j){if(pre&(1<<(j-1))||!ma[x-1][j]){flag=1;break;}}//上一層矩陣左端點或者上一層未連接(與判斷條件各自對應) 
			if(!flag&&((pre&(1<<(up-1))||!ma[x-1][up])))--ret;   //合併 
		}
	}
	return ret;
}
int main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	n=Read(),m=Read();
	int maxx=(1<<m)-1;//從1<<0位開始記錄,則只需要二進制中(m-1)位保存所有狀態 
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			ma[i][j]=Read();
		}
	}
	memset(dp,INF,sizeof(dp));
	dp[0][0]=0;
	for(int i=1;i<=n;++i){//枚舉所有行 
		for(int j=0;j<=maxx;++j){//枚舉當前行的所有狀態 
			if(check(i,j)){//當前這一行的目前狀態合法時,進行接下來的操作 
				for(int k=0;k<=maxx;++k){//枚舉上一行的所有狀態 
					if(dp[i-1][k]!=INF){//若上一行的該狀態有更新過 
						dp[i][j]=min(dp[i][j],dp[i-1][k]+calc(i,j,k));
					}
				}
			}
		}
	}
	int ans=INF;
	for(int i=0;i<=maxx;++i)  ans=min(ans,dp[n][i]);
	cout<<ans;
	return 0;
}

仿寫:

#include<bits/stdc++.h>
using namespace std;
const int N=110,INF=0x3f3f3f3f;
int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=(s<<3)+(s<<1)+ch-'0';
		ch=getchar();
	}
	return s*f;
}
int a[N][10],dp[N][1<<9];
int n,m;
int check(int x,int state){
	for(int i=1;i<=m;i++)
		if((state&(1<<(i-1)))&&!a[x][i])
			return 0;
	int flag=0;
	for(int i=1;i<=m;i++){
		if(state&(1<<(i-1))) flag=1;
		if(a[x][i]&&!flag) return 0;
		if(!a[x][i]) flag=0;
	}
	return 1;
}
int calc(int x,int state,int pre){
	int ret=0;
	for(int i=1;i<=m;i++)
		if(state&(1<<(i-1))) ret++;
	for(int i=1;i<=m;i++){
		if((state&(1<<(i-1)))&&(pre&(1<<(i-1)))){
			int up=i;
			while(a[x][up]&&up<=m+1){
				if(up!=i&&(state&(1<<up-1))) break;
				up++;
			}
			
			int flag=0;
			for(int j=i+1;j<up;j++){if(pre&(1<<(j-1))||!a[x-1][j]){flag=1;break;}}
			if(!flag&&(pre&(1<<(up-1))||!a[x-1][up])) ret--;
		}
	}
	return ret;
}
int main(){
	memset(dp,INF,sizeof dp);
	n=read(),m=read();
	int maxx=(1<<m)-1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) a[i][j]=read();
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=maxx;j++){
			if(check(i,j)){
				for(int k=0;k<=maxx;k++)
				if(dp[i-1][k]!=INF)
				dp[i][j]=min(dp[i][j],dp[i-1][k]+calc(i,j,k)); 
			}
		}
	int ans=INF;
	for(int i=0;i<=maxx;i++) ans=min(ans,dp[n][i]);
	cout<<ans;
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章