题解 CF152E 【Garden】

VenusM1nT

2020-11-02 21:04:28

Solution

好久没做 $\textbf{DP}$ 题了。 ![](https://i.loli.net/2020/11/02/KhlzTStwoWeN7jv.png) 观察到 $k\leq 7$,于是可以考虑状压。以 $0/1$ 代表是否造了当前这座大厦。 可是题目给定的是一个网格图,只考虑是否造的话肯定不对,因此引入 $i,j$ 来代表这个位置是否被覆盖过了。 然后考虑转移。每次枚举 $S,i,j$ 和 $s\in S$,考虑容斥,则得到: $$f_{S,i,j}=f_{s,i,j}+f_{S-s,i,j}-a_{i,j}$$ 这一步是对上一状态的转移,接下来考虑当前状态的计算,要求的是最少毁多少花,其实就是一个最短路,因此每个点从它周围四个点转移过来就可以了,当然这里要多做几次,保证取到最优解(题解里另外一位神仙是 $n\times m+10$ 次,我稍微多循环了几次,也没什么问题)。复杂度 $\text{O}(2^k\times n^2\times m^2)$,因为 $n\times m\leq 200$ 所以没有压力。 ![](https://t1.picb.cc/uploads/2020/10/26/tYMEW7.png) 上述的是最优解的求法,但答案还要求输出方案。不过这题的方案不难输,可以记录每个点每个状态的前驱,最后递归一下就可以求出来了。 ![](https://t1.picb.cc/uploads/2020/10/26/tY8nSc.png) ```cpp #include<bits/stdc++.h> #define N 100 #define St 200 #define reg register #define inl inline using namespace std; int n,m,K,f[N+5][N+5][St],pre[N+5][N+5][St][3],a[N+5][N+5]; int dx[]={1,-1,0,0},dy[]={0,0,1,-1}; bool res[N+5][N+5]; void Print(reg int x,reg int y,reg int S) { res[x][y]=1; if(pre[x][y][S][0]==2) return; if(!pre[x][y][S][0]) Print(pre[x][y][S][1],pre[x][y][S][2],S); else { Print(x,y,pre[x][y][S][1]); Print(x,y,pre[x][y][S][2]); } } int main() { scanf("%d %d %d",&n,&m,&K); for(reg int i=1;i<=n;i++) { for(reg int j=1;j<=m;j++) scanf("%d",&a[i][j]); } for(reg int S=0;S<(1<<K);S++) { for(reg int i=1;i<=n;i++) { for(reg int j=1;j<=m;j++) f[i][j][S]=1e9; } } for(reg int i=1;i<=K;i++) { reg int x,y; scanf("%d %d",&x,&y); f[x][y][1<<(i-1)]=a[x][y]; pre[x][y][1<<(i-1)][0]=2; } for(reg int S=0;S<(1<<K);S++) { for(reg int i=1;i<=n;i++) { for(reg int j=1;j<=m;j++) { for(reg int k=S;k;k=(k-1)&S) { reg int cnt=f[i][j][k]+f[i][j][S^k]-a[i][j]; if(cnt<f[i][j][S]) { f[i][j][S]=cnt; pre[i][j][S][0]=1; pre[i][j][S][1]=k; pre[i][j][S][2]=S^k; } } } } for(reg int o=1;o<=n*m+N;o++) { for(reg int i=1;i<=n;i++) { for(reg int j=1;j<=m;j++) { for(reg int k=0;k<4;k++) { reg int nx=i+dx[k],ny=j+dy[k]; if(nx<1 || nx>n || ny<1 || ny>m) continue; reg int cnt=f[nx][ny][S]+a[i][j]; if(cnt<f[i][j][S]) { f[i][j][S]=cnt; pre[i][j][S][0]=0; pre[i][j][S][1]=nx; pre[i][j][S][2]=ny; } } } } } } reg int ans=1e9; for(reg int i=1;i<=n;i++) { for(reg int j=1;j<=m;j++) ans=min(ans,f[i][j][(1<<K)-1]); } printf("%d\n",ans); for(reg int i=1;i<=n;i++) { for(reg int j=1;j<=m;j++) { if(f[i][j][(1<<K)-1]==ans) { Print(i,j,(1<<K)-1); for(reg int k=1;k<=n;k++) { for(reg int l=1;l<=m;l++) printf("%c",res[k][l]?'X':'.'); puts(""); } return 0; } } } } ```