題目鏈接
題解:這題其實會狀壓dp就很簡單了,只是限制條件有點多。
- 先預處理出一行的每一個合法的狀態,這個基本操作,左移一位,兩位,右移一位,兩位,判斷一下就好了。
- 設dp[i][j][k],意思是在第i行,第i行的狀態爲j,第i - 1行狀態爲k能放的最多炮陣。
- 然後轉移要三層循環,比如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;
}