CF152E:状态压缩

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 
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<q;
void Init(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i 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

 

你可能感兴趣的:(DP)