洛谷P1283 平板涂色题解
本题是状态压缩的dp题目,具体注释在代码当中,有挺多细节
#include<iostream> #include<string> #include<vector> #include<cstdio> #include<queue> #include<algorithm> #include<cstring> 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<y2[i];j++){ //这里要注意不是j<=y2[i],因为假如有两个矩形的边重叠了的情况,我们默认只使用一边 for(k=x1[i];k<x2[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<<n)-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<<n)-1][i]); } cout<<ans<<endl; }
没有人不辛苦,只有人不喊疼