P2704 [NOI2001]炮兵阵地(状压dp)

题目链接

题解:这题其实会状压dp就很简单了,只是限制条件有点多。

  1. 先预处理出一行的每一个合法的状态,这个基本操作,左移一位,两位,右移一位,两位,判断一下就好了。
  2. 设dp[i][j][k],意思是在第i行,第i行的状态为j,第i - 1行状态为k能放的最多炮阵。 
  3. 然后转移要三层循环,比如dp[i][j][k] 能被 dp[i - 1][k][z] 转移的条件是,i不能打到j,以及i不能打到k。

我讲的或许比较随意,推荐刷掉以下几个题再来试试这个题。

P1879  P1896  刷完之后,简直分分钟切掉这个题  代码我也贴后面了

//P2704 [NOI2001]炮兵阵地
#include
using namespace std;
const int M = (1 << 11), N = 110;
int dp[N][N][N];
int sta[N], cnt[M], F[M];
char s[N][20];
int main()
{
	int n, m, mx, tot = 0;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i)
	scanf("%s", s[i] + 1);
	for(int i = 1; i <= n; ++i)
	for(int j = 1; j <= m; ++j)
	F[i] = (F[i] << 1) + (s[i][j] == 'P');
	
	mx = (1<>1)) && !(i&(i>>2)));
		if(tmp) sta[++tot] = i;
		tmp = i;
		while(tmp){
			if(tmp % 2) cnt[i]++;
			tmp/=2;
		}
	}
	for(int i = 1; i <= tot; ++i)
	for(int j = 1; j <= tot; ++j)
	if((F[1] & sta[i]) == sta[i])
	dp[1][i][j] = cnt[sta[i]];
	
	for(int i = 2; i <= n; ++i)
	for(int j = 1; j <= tot; ++j)
	if((sta[j] & F[i]) == sta[j])
	for(int k = 1; k <= tot; ++k)
	for(int z = 1; z <= tot; ++z){
		int sta1 = sta[j];
		int sta2 = sta[k];
		int sta3 = sta[z];
		if(dp[i - 1][k][z] && !(sta1 & sta2) && !(sta1 & sta3))
		dp[i][j][k] = max(dp[i - 1][k][z] + cnt[sta1], dp[i][j][k]);
	}
	
	int ans = 0;
	for(int i = 1; i <= tot; ++i)
	for(int j = 1; j <= tot; ++j)
	ans = max(ans, dp[n][i][j]);
	printf("%d\n", ans);
}

 

//P1879 [USACO06NOV]玉米田Corn Fields

#include
using namespace std;
typedef long long ll;
const int N = (1 << 12), mod = 1e9;
int dp[20][N], vis[N], F[20], mp[20][20], n, m; 
int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i)
	for(int j = 1; j <= m; ++j){
		scanf("%d", &mp[i][j]);
		F[i] = (F[i]<<1) + mp[i][j];
	}
	int mx = (1 << m);
	for(int i = 0; i < mx; ++i)
		vis[i] = (!(i&(i<<1)) && !(i&(i>>1)));
	dp[0][0] = 1;
	for(int i = 1; i <= n; ++i)
	for(int j = 0; j < mx; ++j)
	if(vis[j] && (j & F[i]) == j)
	for(int k = 0; k < mx; ++k)
	if((j & k) == 0)
	dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
	ll ans = 0;
	for(int i = 0; i < mx; ++i)
		ans = (ans + dp[n][i]) % mod;
	cout<

 

P1896 [SCOI2005]互不侵犯
#include
using namespace std;
typedef long long ll;
const int M = (1 << 10);
ll dp[15][M][110], sta[M], need[M];
int n, k, mx;
int check(int j, int i){
	int flag = 0;
	if(!sta[i]) flag = 1;
	if((i & j)) flag = 1;
	if((i & (j<<1))) flag = 1;
	if((i & (j>>1))) flag = 1;
	return flag;
}
int main()
{
	cin >> n >> k;
	mx = (1 << n);
	for(int i = 0; i < mx; ++i){
		sta[i] = (!(i&(i<<1)) && !(i&(i>>1)));
		int tmp = i;
		while(tmp){
			if(tmp % 2) need[i]++;
			tmp /= 2;
		}
	}
	for(int i = 0; i < mx; ++i)
		if(sta[i] && need[i] <= k)
			dp[1][i][need[i]] = 1;
	for(int i = 2; i <= n; ++i)
	for(int j = 0; j < mx; ++j)
	if(sta[j])
	for(int z = 0; z < mx; ++z){
		if(check(j, z)) continue;
		for(int tot = k; tot >= need[j]; --tot)
			dp[i][j][tot] += dp[i - 1][z][tot - need[j]];
	}
	ll ans = 0;
	for(int i = 0; i < mx; ++i)
		ans += dp[n][i][k];
	cout << ans << endl;
}

 

你可能感兴趣的:(状压dp)