洛谷P1283 平板涂色题解

本题是状态压缩的dp题目,具体注释在代码当中,有挺多细节

#include
#include<string>
#include
#include
#include
#include
#include
using namespace std;
const int N=1<<16+1;
const int M=21;
const int inf=0x3f3f3f3f;
int f[N][21];
int up[M][M];
int num[M];
int col[M];
int g[110][110]; 
int x1[M],x2[M],y1[M],y2[M];
int sign;
bool check(int u,int x){ //检查是否满足上面都涂了条件 
    bool flag=true;
    for(int i=1;i<=num[x];i++)
        if(!((1<<(up[x][i]-1))&u)){
        flag=false;    
        break;
        }
        
    return flag;
}
int main(){
    int i;
    int j,k;
    int n;
    cin>>n;
    for(i=1;i<=n;i++){
        cin>>y1[i]>>x1[i]>>y2[i]>>x2[i]>>col[i];
        sign=max(sign,col[i]);
        for(j=y1[i];j//这里要注意不是j<=y2[i],因为假如有两个矩形的边重叠了的情况,我们默认只使用一边 
            for(k=x1[i];k)
            g[j][k]=i;
        }
    }
    for(i=1;i<=n;i++){  //将上方的情况枚举出来 
        if(!y1[i])
        continue;
        int u=y1[i]-1;
        for(j=x1[i];j<x2[i];){
            if(g[u][j]){
            int l=j;
            while(g[u][j]==g[u][l]&&l<x2[i])
            l++;
            up[i][++num[i]]=g[u][j];
            j=l;    
            }
        }
    }
    memset(f,40,sizeof f);
    for(i=1;i<=sign;i++)
    f[0][i]=1; //对于未涂色的情况,刚开始的任何一种颜色的次数都是1 
    for(i=1;i<=(1<1;i++){ //枚举所有涂色情况,这样就可以遍历所有状态 
        for(j=1;j<=n;j++){  //枚举涂每块的情况 
            if(((1<<(j-1))&i)==(1<<(j-1))&&check(i,j)){ //检查该位置是否已经涂上该颜色,只有涂上了才可以用其他状态转移他
                                                         //还需上面都涂过 
                for(k=1;k<=sign;k++){  //枚举之前的是那种颜色 
                    if(k!=col[j]){  
                        f[i][col[j]]=min(f[i-(1<<(j-1))][k]+1,f[i][col[j]]);
                    }
                }
                f[i][col[j]]=min(f[i][col[j]],f[i-(1<<(j-1))][col[j]]);
                
            }
        }
    }
    int ans=inf;
    for(i=1;i<=sign;i++){
        ans=min(ans,f[(1<1][i]);
        
    }
    cout<endl;
}
View Code

 

你可能感兴趣的:(洛谷P1283 平板涂色题解)