DP做题笔记

2026.5

5.13:

1.P8756 [蓝桥杯 2021 省 AB2] 国际象棋

说实话,状压dp大多数题还是很明显的,这道题就不例外,典型的棋盘上状压。

定义 \(dp[i][s][j][k]\) 表示第 \(i\) 行状态为 \(s\),上一行状态为 \(j\),放了 \(k\) 个马的方案数。

转移方程:\(dp[i][s][j][k] = dp[i][s][j][k] + dp[i - 1][j][h][k - cnt[s]]\),其中 \(h\) 表示上上行的状态,\(cnt[s]\) 表示状态 \(s\) 的1的个数。

Ac Code
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = (1 << 6);
const int mod = 1e9 + 7; 
inline int read(){
	char ch; int f = 1,res = 0; ch = getchar();
	while (!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while (isdigit(ch)) {res = res * 10 + (ch - '0'); ch = getchar();}
	return res * f;
}
inline void write(int x){
    if (x < 0) putchar('-'),x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}
int n,m,K;
long long ans = 0;
long long dp[110][maxn][maxn][30];
signed main(){
	memset(dp,0,sizeof(dp));
	n = read(); m = read(); K = read();
	for (int i = 0;i < (1 << n);i++) dp[1][i][0][__builtin_popcount(i)] = 1;
	for (int i = 2;i <= m;i++){
		for (int S = 0;S <= (1 << n) - 1;S++){
			for (int j = 0;j <= (1 << n) - 1;j++){
				if (S & (j << 2) || S & (j >> 2)) continue;
				for (int k = 0;k <= (1 << n) - 1;k++){
					if (S & (k << 1) || S & (k >> 1) || j & (k << 2) || j & (k >> 2)) continue;
					for (int h = __builtin_popcount(S);h <= K;h++){
						dp[i][S][j][h] += dp[i - 1][j][k][h - __builtin_popcount(S)];
						dp[i][S][j][h] %= mod;
					}
				}
			}
		}
	}
	for (int j = 0;j < (1 << n);j++){
		for (int k = 0;k < (1 << n);k++){
			ans = (ans + dp[m][j][k][K]) % mod;
		}
	} 
	cout << ans;
	return 0;
}

2.P2704 [NOI2001] 炮兵阵地

近似于板子的状压dp,只是需要关注上面的两行。

定义 \(dp[i][s][j]\) 表示第 \(i\) 行,状态为 \(s\),上行状态为 \(j\),所能放的最多炮兵。

转移方程:\(dp[i][s][j] = max(dp[i][s][j],dp[i - 1][j][k] + cnt[s])\),其中 \(k\) 表示上上行的状态,\(cnt[s]\) 表示 \(s\) 状态的1的个数。

Ac Code
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = (1 << 6);
inline int read(){
	char ch; int f = 1,res = 0; ch = getchar();
	while (!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while (isdigit(ch)) {res = res * 10 + (ch - '0'); ch = getchar();}
	return res * f;
}
inline void write(int x){
    if (x < 0) putchar('-'),x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}
int n,m,t = 0;
int mp[110],dp[110][maxn][maxn];
int zt[maxn],cnt[maxn];
signed main(){
	n = read(); m = read();
	memset(dp,0,sizeof(dp));
	mp[0] = (1 << m) - 1;
	for (int i = 1;i <= n;i++){
		string s; cin >> s;
		mp[i] = (s[0] == 'P');
		for (int j = 1;j < m;j++) mp[i] = (mp[i] << 1) | (s[j] == 'P');
	}
	for (int i = 0;i < (1 << m);i++){
		if (i & (i << 1) || i & (i << 2) || i & (i >> 1) || i & (i >> 2)) continue;
		zt[++t] = i; cnt[t] = __builtin_popcount(i);
	}
	for (int i = 1;i <= t;i++){
		if ((~mp[1]) & zt[i]) continue;
		dp[1][i][1] = cnt[i];
	}
	for (int h = 2;h <= n;h++){
		for (int i = 1;i <= t;i++){
			if (zt[i] & (~mp[h])) continue;
			for (int j = 1;j <= t;j++){
				if (zt[j] & (~mp[h - 1]) || zt[i] & zt[j]) continue;
				for (int k = 1;k <= t;k++){
					if (zt[k] & (~mp[h - 2]) || zt[i] & zt[k] || zt[j] & zt[k]) continue;
					dp[h][i][j] = max(dp[h][i][j],dp[h - 1][j][k] + cnt[i]);
				}
			}
		}
	}
	int ans = 0;
	for (int i = 1;i <= t;i++){
		for (int j = 1;j <= t;j++){
			ans = max(ans,dp[n][i][j]);
		}
	}
	cout << ans;
	return 0;
}
posted @ 2026-05-13 19:54  WJX120423  阅读(8)  评论(0)    收藏  举报