codeforces 111C(状压DP)
思路:数据范围很小,考虑用状压来做...这类网格放东西问题其实dp大概都可做。
把问题转化为蜘蛛移动后,每个点上下左右和本身至少有一个网格有蜘蛛。
首先取n,m的较小值作为列数,较大值作为行数,对每行进行状压(即用一个二进制数表示),0表示没有蜘蛛,1表示有蜘蛛。
用dp[i][j][k]表示当前处理第i行,且第i行状态为j,第i+1行状态为k时,前i行的空网格数。
那么只要逐行枚举j:0~(1<<(m-1)) - 1,k:0~(1<<(m-1)) - 1,再枚举i+2行的状态L:0~(1<<(m-1)) - 1
然后检查第i+1行的可行性,(若发现某个位置自己和上下左右五个网格均无蜘蛛则不可行),DP转移方程:
dp[i+1][k][L] = max(dp[i+1][k][L],dp[i][j][k] + Get(k)); 其实:Get(k)就是获取k的二进制中有多少位为0。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <vector> 6 #include <map> 7 #include <set> 8 #include <stack> 9 #include <queue> 10 #include <string> 11 #include <iostream> 12 #include <algorithm> 13 using namespace std; 14 15 #define MEM(a,b) memset(a,b,sizeof(a)) 16 #define REP(i,n) for(int i=1;i<=(n);++i) 17 #define REV(i,n) for(int i=(n);i>=1;--i) 18 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 19 #define RFOR(i,a,b) for(int i=(a);i>=(b);--i) 20 #define MP(a,b) make_pair(a,b) 21 22 typedef long long ll; 23 typedef pair<int,int> pii; 24 const int INF = (1 << 30) - 1; 25 26 int n,m,ans,top; 27 int dp[45][100][100]; 28 29 bool Check(int j,int k,int l){ 30 int a,b,c,d,e; 31 FOR(i,0,n - 1){ 32 a = j & (1 << i); 33 b = k & (1 << i); 34 c = l & (1 << i); 35 d = (i == 0) ? 0 : (k & (1 << (i - 1))); 36 e = (i == n - 1) ? 0 : (k & (1 << (i + 1))); 37 if(!(a || b || c || d || e)) return false; 38 } 39 return true; 40 } 41 42 int Get(int v){ 43 int cnt = 0; 44 FOR(i,0,n - 1) cnt += !(v & (1 << i)); 45 return cnt; 46 } 47 48 int main(){ 49 scanf("%d%d",&n,&m); 50 if(n > m) swap(n,m); 51 top = (1 << n) - 1; 52 FOR(i,0,top) FOR(j,0,top) dp[0][i][j] = -INF; 53 FOR(i,0,top){ 54 dp[0][0][i] = 0; 55 } 56 FOR(i,0,m){ 57 FOR(j,0,top) FOR(k,0,top) FOR(l,0,top){ 58 if(Check(j,k,l)){ 59 dp[i + 1][k][l] = max(dp[i + 1][k][l], 60 dp[i][j][k] + Get(k)); 61 //printf("dp[%d][%d][%d]:%d -> %d\n",i,j,k,dp[i][j][k],l); 62 } 63 } 64 } 65 FOR(i,0,top) ans = max(ans,dp[m][i][0]); 66 printf("%d\n",ans); 67 return 0; 68 }