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<bits/stdc++.h>
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<<m);
	
	for(int i = 0; i < mx; ++i){
		int tmp = (!(i&(i<<1)) && !(i&(i<<2)) && !(i&(i>>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<bits/stdc++.h>
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<<ans<<endl;
}

 

P1896 [SCOI2005]互不侵犯
#include<bits/stdc++.h>
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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章