CF152E
题意:翻转'.'为'X',使所有的'X'都相连,并且使花费最少。
题解y
- dp[x][y][z]表示点(x,y)分割状态z为两部分s1和s2,两部分都经过点(x,y),并且满足s1&s2 = 0,s1^s2 = z。也就是说s1和s2无交集,其并集为z。有k个点是必须要经过的,状态z表示经过哪几个点的状态。
- 状态转移1:dp[x]][y][z] =min(dp[x][y][z],dp[x][y][s1] + dp[x][y][s2] - cost[x][y])。因为两者都经过(x,y)所以要减。
- 状态转移2:点(x,y)向四个方向转移为dp[dx][dy][z],所以dp[dx][dy][z] = min(dp[dx][dy][dz],dp[x][y][z] + cost[dx][dy])
- 每次进队列要打上标记,出队列要使标记清除,因为不一定是最优的。
- pre[x][y][z]表示点(x,y)分割状态z的上一个状态。用于回溯
- 详细见代码。
代码
#include <bits/stdc++.h>
using namespace std;
int const inf = 0x7f7f7f7f;
int const N = 100 + 10;
int const M = 8;
int n,m,k;
int dir[4][2] = {0,1,1,0,0,-1,-1,0};
int cost[N][N],dp[N][N][1<<M]; //dp[x][y][z]表示点(x,y)分割状态为两部分
bool use[N][N],vis[N][N][1<<M];
struct Node
{
int x,y,s; //坐标和状态
Node(){}
Node(int x,int y,int s):x(x),y(y),s(s){};
}pre[N][N][1<<M]; //记录上一个状态
queue<Node>q;
void Init(){
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%d",&cost[i][j]);
for(int i=0;i<n;i++){ //初始化
for(int j=0;j<m;j++){
for(int s=0;s<(1<<k);s++){
dp[i][j][s] = inf;
pre[i][j][s] = Node(-1,-1,-1); //s = -1表示没有上一个状态
}
}
}
for(int i=0;i<k;i++){
int x,y;
scanf("%d%d",&x,&y);
x--,y--; //输出从1开始的
dp[x][y][(1<<i)] = cost[x][y];
use[x][y] = true; //变成水泥
q.push(Node(x,y,1<<i));
vis[x][y][(1<<i)] = true; //进入队列
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!use[i][j]){
dp[i][j][0] = cost[i][j];
q.push(Node(i,j,0));
vis[i][j][0] = true;
}
}
}
}
void bfs(){
while(!q.empty()){
Node p = q.front(); q.pop();
vis[p.x][p.y][p.s] = false; //不一定是最优的
for(int s=1;s<(1<<k);s++){
if(p.s & s) continue;
int ss = p.s ^ s; //把p.s和s中含有1合并 //
if(dp[p.x][p.y][ss] > dp[p.x][p.y][p.s] + dp[p.x][p.y][s] - cost[p.x][p.y]){
dp[p.x][p.y][ss] = dp[p.x][p.y][p.s] + dp[p.x][p.y][s] - cost[p.x][p.y];
pre[p.x][p.y][ss] = Node(s,p.s,0); //0表示加分割的点没有变化,但是两个状态合并
if(vis[p.x][p.y][ss]) continue;
vis[p.x][p.y][ss] = true;
q.push(Node(p.x,p.y,ss));
}
}
for(int i=0;i<4;i++){
int dx = p.x + dir[i][0], dy = p.y + dir[i][1];
if(dx < 0 || dx >= n || dy < 0 || dy >= m) continue;
if(dp[dx][dy][p.s] > dp[p.x][p.y][p.s] + cost[dx][dy]){
dp[dx][dy][p.s] = dp[p.x][p.y][p.s] + cost[dx][dy];
pre[dx][dy][p.s] = Node(p.x,p.y,1); //1表示加入一个点,分割的点变化
if(vis[dx][dy][p.s]) continue;
vis[dx][dy][p.s] = true;
q.push(Node(dx,dy,p.s));
}
}
}
}
void getpre(int x,int y,int s){
if(pre[x][y][s].s == -1) return;
if(pre[x][y][s].s == 0){
getpre(x,y,pre[x][y][s].x);
getpre(x,y,pre[x][y][s].y);
}else{
use[x][y] = true;
getpre(pre[x][y][s].x,pre[x][y][s].y,s);
}
}
void solve(){
Node ans;
int mx = inf;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(dp[i][j][(1<<k)-1] < mx){ //求出最优的方案
mx = dp[i][j][(1<<k)-1];
ans = Node(i,j,(1<<k)-1);
}
}
}
printf("%d\n",mx);
getpre(ans.x,ans.y,ans.s); //回溯
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
printf("%c",use[i][j] ? 'X' : '.');
printf("\n");
}
}
int main(){
Init();
bfs();
solve();
return 0;
}