描述
【題目背景】
NEWorld作爲一個3D遊戲,對渲染(圖形繪製)的效率要求極高。當玩家擴大視野範圍時,可見的方塊面數量將會迅速增多,以至於大量的頂點處理很快就成爲了圖形管線中的瓶頸。喬貓想了想,決定在大量繪製前,預處理一些相鄰且有着相同材質的方塊面——將許多小的面合成一個大的面,便可以在不改變渲染結果的同時減少很多頂點數量了吧……
【問題描述】
給定一個的網格,每個格子上寫有。現在用一些長方形覆蓋其中寫有的格子,長方形的每條邊都要與座標軸平行。要求:每個寫着的格子都要被覆蓋,長方形不可以重疊(重複繪製也多少會增加性能開銷),也不能覆蓋到任何一個寫着的格子(不然繪製結果就不正確了)。請問最少需要多少長方形?
輸入
輸入文件第一行兩個正整數,表示網格大小爲。
接下來的行,每行個正整數,其中的正整數表示網格裏填的數。
輸出
輸出文件包含一行一個正整數,表示最少需要的長方形數量。
樣例輸入
4 4
1 1 1 0
1 1 1 1
0 0 1 1
0 0 1 1
樣例輸出
3
提示
對於 %的數據
對於 %的數據
解析:
看到的範圍很小,就要有條件反射:狀壓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;
}