题目链接【http://codeforces.com/problemset/problem/111/C】
题意:给出大小为N*M的图(1 ≤ n, m ≤ 40, n·m ≤ 40),每个图中有一个蜘蛛,每个蜘蛛有5种运动状态,不动,向上下左右移动。问蜘蛛如何移动才能使得图中的空地数最大,输出最大空地数。
题解:虽然(1 ≤ n, m ≤ 40, n·m ≤ 40),但是当(n<m)的时候可以swap(n,m),对结果是没有影响的。用一个二进制数表示每一行的每个位置的状态。最多有(1<<6)-1种状态,如果该位置是1 表示该位置可以容纳蜘蛛,反之不能放入蜘蛛。
dp[i][j][k]为第i行的状态为j,i+1行的状态为k的空位数。 dp[i + 1][k][l] = max(dp[i + 1][k][l], dp[i][j][k] + nu[k]);这个第(i+1)行的转移方程是满足在第i、第(i+1)行、第(i+2)行的状态使得(i+1)行的蜘蛛的有去处。最后取DP[n][i][0]的最大值。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 1 << 6;//6*7=42; int dp[45][maxn][maxn];//dp[i][j][k]为第i行的状态为j,i+1行的状态为k的空位数 int nu[maxn]; int n, m, lim; bool OK (int a, int b, int c) { int t = b | (b << 1) | (b >> 1) | a | c; return (t&lim) == lim; } int main () { scanf("%d%d", &n, &m); if(n < m) swap(n, m); memset(dp, -1, sizeof(dp)); lim = (1 << m) - 1; for(int i = 0; i <= lim; i++) { dp[0][0][i] = 0; int t = i; while(t) { if(t & 1) nu[i]++; t >>= 1; } nu[i] = m - nu[i]; } for(int i = 0; i < n; i++) for(int j = 0; j <= lim; j++) //i行的状态 for(int k = 0; k <= lim; k++) //i+1行的状态 if(dp[i][j][k] != -1) { for(int l = 0; l <= lim; l++) //i+2行的状态 if(OK(j, k, l)) dp[i + 1][k][l] = max(dp[i + 1][k][l], dp[i][j][k] + nu[k]); } int ans = 0; for(int i = 0; i <= lim; i++) ans = max(ans, dp[n][i][0]); printf("%d ", ans); return 0; }