做完[NOI2001]炮兵阵地这道题,顿时觉得此题好水~~15分钟AC
令dp[i][j][k]表示到第 i 行放了 j 个国王,该行状态为 k 时的方案数。然后dp的时候第一层循环枚举 i,第二层 j,如果j 合法的话,再枚举第三层 i - 1行的状态 k,然后如果k合法且 j 和k不冲突的话,再枚举国王数量h,于是就有:
dp[i][h][j] += dp[i - 1][h - sum[j]][k]
至于如何判断j和k不冲突,那自然是 j & k == 0 且 j | k状态是合法的。
优化就是先预处理所有合法的 j 和k。
然后别忘了dp[1]单独处理。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a) memset(a, 0, sizeof(a)) 15 typedef long long ll; 16 typedef double db; 17 const int INF = 0x3f3f3f3f; 18 const db eps = 1e-8; 19 const int max_sta = 100; //经测试。合法的状态最多只有89种 20 inline ll read() 21 { 22 ll ans = 0; 23 char ch = getchar(), last = ' '; 24 while(!isdigit(ch)) {last = ch; ch = getchar();} 25 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 26 if(last == '-') ans = -ans; 27 return ans; 28 } 29 inline void write(ll x) 30 { 31 if(x < 0) x = -x, putchar('-'); 32 if(x >= 10) write(x / 10); 33 putchar(x % 10 + '0'); 34 } 35 36 int n, m; 37 int sum[max_sta], s[max_sta], cnt = 0; 38 bool ok[1000]; 39 ll dp[10][100][max_sta]; 40 41 int getsum(int x) 42 { 43 int ret = 0; 44 for(; x; x >>= 1) ret += x & 1; 45 return ret; 46 } 47 void init() 48 { 49 for(int i = 0; i < (1 << n); ++i) 50 if(!(i & (i << 1)) && !(i & (i >> 1))) 51 { 52 s[++cnt] = i; 53 ok[i] = 1; 54 sum[cnt] = getsum(i); 55 dp[1][sum[cnt]][cnt]++; 56 } 57 } 58 59 int main() 60 { 61 n = read(); m = read(); 62 init(); 63 for(int i = 2; i <= n; ++i) 64 for(int j = 1; j <= cnt; ++j) 65 for(int k = 1; k <= cnt; ++k) 66 if(!(s[j] & s[k]) && ok[s[j] | s[k]]) 67 for(int h = sum[j]; h <= m; ++h) 68 dp[i][h][j] += dp[i - 1][h - sum[j]][k]; 69 ll ans = 0; 70 for(int i = 1; i <= cnt; ++i) ans += dp[n][m][i]; 71 write(ans); enter; 72 return 0; 73 }