现在的位置: 首页 > 综合 > 正文

状态压缩DP(二)

2017年12月13日 ⁄ 综合 ⁄ 共 3796字 ⁄ 字号 评论关闭

以前没做过这种类型的状态压缩DP,刚开始看着没想明白,后来跟着代码看着好多了。

题目:poj1185兵阵地

题意:一个矩阵,有一些地方可以放大炮,有的地方不能放,大炮对上下左右的攻击范围都是两格。两个大炮不能互相攻击到,问最多放多少大炮。

思路:求出每一行的所有状态数,即用二进制表示的所有可行状态(1010,选第一个和第三个)。大炮的攻击距离是2,所以本行、上一行、上上一行不能有大炮在同一行。遍历本行、上一行、上上一行,计算并记下可行状态的结果。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <string>
using namespace std;
#define For(i,a) for(i=0;i<a;i++)
#define Foru(i,a,b) for(i=a;i<=b;i++)
#define Ford(i,a,b) for(i=a;i>=b;i--)
#define PB push_back
#define INF 0x7fffffff

int n, m;
int cnt[120];
int now[102],last[102],lastlast[102];
int dp[120][120], lastdp[120][120];
char g[102][12];
int nowsize, lastsize, lastlastsize;
void init(int i, int j, int p, int sum){
	if( j > m) { now[nowsize] = p; cnt[nowsize++] = sum; return ;}
	if( g[i][j] == 'P' ) init(i, j+3, p|1<<j, sum+1);
	init(i, j+1, p, sum);
}

void DP(){
	for(int i = 0; i < n; i ++){
		nowsize = 0;
		init(i,0,0,0);
		//cout << nowsize << endl;
		memset(dp, 0, sizeof(dp));
		for(int j = 0; j < nowsize; j ++) {
			for(int k = 0; k < lastsize; k ++){
				for(int t = 0; t < lastlastsize; t ++){
					if( now[j] & last[k]  || now[j] & lastlast[t] || last[k] & lastlast[t]) continue;
					dp[j][k] = max(dp[j][k], lastdp[k][t] + cnt[j] );
				//	lastdp[j][k] = dp[j][k];
				}
			}
		}
		int k, j;
		For(k, nowsize) For(j, lastsize) lastdp[k][j] = dp[k][j];
		
		for(int j = 0; j < lastsize; j ++) lastlast[j] = last[j]; lastlastsize = lastsize;
		
		for(int j = 0; j < nowsize; j ++) last[j] = now[j]; lastsize = nowsize;
		
		//for(int j = 0; j < nowsize; j ++) lastdp[
		
	}
}
		
int main(){
	while( ~scanf("%d%d",&n,&m)){
		for(int i = 0; i < n; i ++) scanf("%s",g[i]);
		//memset(lastdp, 0, sizeof(lastdp));
		last[0]=lastlast[0]=lastdp[0][0]=0; 
		lastsize = lastlastsize = 1;
		DP();
		int ans = 0;
		for(int i = 0; i < lastsize; i ++){
			for(int j = 0; j < lastlastsize; j ++){
				ans = max( ans, lastdp[i][j]);
			}
		}
		cout << ans << endl;
	}
	return 0;
}

题目:ACdream Chess

题意:有一个棋盘,棋盘上有些点能放棋子,有些点不可以。一个棋子能够控制相邻的四个位置,两个棋子不能互相控制。求最少多少个棋子能控制全盘?

思路:先学的上面的,根据上面的思路卡在了控制全盘。需要在上面的代码上加入控制的状态,nowvis[i][j] 本行选i上一行选j本行能控制的格子状态。now[i]和last[j]计算的时候上面一行要全部控制到,因为在下面一行不可能控制到last[j]行。最后求全控制的最小值。群里面大神用dancing links 做的,正在学习中……

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <string>
using namespace std;
#define For(i,a) for(i=0;i<a;i++)
#define Foru(i,a,b) for(i=a;i<=b;i++)
#define Ford(i,a,b) for(i=a;i>=b;i--)
#define PB push_back
#define INF 0x7fffffff
int ans;
int n, m, all;
bool g[10][10];

int last[100],now[100];
int lastsize, nowsize, lastlastsize;
int cnt[100],dp[110][110],lastdp[100][110];
int bit[10],nowvis[110][110],lastvis[110][110];

int getbit(int p){
	int i = 0, res = 0;
	while(p){
		bit[i++] = p%2;
		p /= 2;
	}
	if( bit[0] == 1)  res |= 3;
	for(int j = 1; j < i; j ++) if(bit[j] == 1) res |= 7<<j-1;
	if(res > all) res = res & (res & all);
	return res;
}

void init(int i, int j, int p, int sum){
	if( j >= n) { now[nowsize] = p; cnt[nowsize++] = sum; return ;}
	if( g[i][j] == 0 )	init(i, j+2, p|1<<j, sum+1);
	init(i, j+1, p, sum);
}

void DP(){
	for(int i = 0; i < n; i ++){
		nowsize = 0;
		memset(nowvis,0,sizeof(nowvis));
		memset(bit,0,sizeof(bit));
		memset(dp, 0x3f, sizeof(dp));
		init(i, 0, 0, 0);
		
	
		for(int j = 0; j < nowsize; j ++){
			for(int k = 0; k < lastsize; k ++){
				for(int t = 0; t < lastlastsize; t ++){
					if( now[j] & last[k] ) continue;
					
					if((lastvis[k][t] | now[j] )== all) {
						dp[j][k] = min(dp[j][k], lastdp[k][t] + cnt[j]);
						nowvis[j][k] = getbit(now[j]) | last[k];
					}
				}
			}
		}
		//for(int j = 0; j < nowsize; j ++) {for(int k = 0; k < lastsize; k ++) cout << dp[j][k] << ' ' ;cout <<endl;}cout << endl << endl ;
		lastlastsize = lastsize;
		for(int j = 0; j < nowsize; j ++)	last[j] = now[j];
		for(int j = 0; j < nowsize; j ++) 	for(int k = 0; k < lastsize; k ++) lastdp[j][k] = dp[j][k];
		for(int j = 0; j < nowsize; j ++) 	for(int k = 0; k < lastsize; k ++) lastvis[j][k] = nowvis[j][k];
		lastsize = nowsize;

	}
}
int main(){
	int x, y; 
	while(cin >> n >> m){
		all = 1<< n;
		ans  = 0x7fffffff;
		all --;

		memset(g,0,sizeof(g));
		for(int i = 0; i < m; i ++) {
			cin >> x >> y;
			g[x-1][y-1] = 1;
		}
		last[0] = dp[0][0] = lastdp[0][0] = 0;
		lastvis[0][0] = all;
		lastsize = lastlastsize = 1;
		
		DP();
		for(int i = 0; i < nowsize; i ++){
			for(int j = 0; j < lastsize; j ++){
				if(lastvis[i][j] == all) ans = min(dp[i][j], ans);
			}
		}
		if( ans > 100) cout << -1 << endl;
		else cout << ans << endl;
	}
	return 0;
}

抱歉!评论已关闭.