题目链接【http://poj.org/problem?id=1038】
题意: 给出一个N*M大小的图,图中有K个坏点。N (1 <= N <= 150), M (1 <= M <= 10), K (0 <= K <= MN);用2*3和3*2的木块去填这个图,问最多能放多少个木块。
题解:用一个三进制数表示某一行的状态,如果pos位置是0:表示该位置被占用了,pos位置是1:表示该位置没有被占用但是上一层的pos位置被占用了,pos位置是2:表示该位置和上一层的该位置否没有被占用。
用数组last[]和now[]表示上一层和本层的状态(用编码器和解码器实现)如果该位置为坏点那么now[pos]=0,否则now[pos]=min(2,last[pos]+1);用DFS实现,横着放,竖着放,或者不放,最后取最后一行每个状态下的值,取最大。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 60050; int dp[2][maxn], cur; int last[15], now[15]; int mp[155][15]; int T, Last, lim; int n, m, k; int en_code()//编码器 { int ans = 0; for(int i = m - 1; i >= 0; i--) ans = ans * 3 + now[i]; return ans; } void de_code(int x)//译码器 { for(int i = 0; i < m; i++) { last[i] = x % 3; x /= 3; } } void DFS(int pos, int val, int r) { if(pos == m)//本行已经处理完 { int j = en_code(); dp[cur][j] = max(dp[cur][j], dp[cur ^ 1][Last] + val); return ; } if(mp[r][pos])//如果当前位置是坏点,直接跳过 { now[pos] = 0; DFS(pos + 1, val, r); return ; } now[pos] = min(2, last[pos] + 1);//当前行pos点的状态 if(pos > 1 && now[pos - 1] == 2 && now[pos - 2] == 2 && now[pos] == 2)//横着放 { now[pos] = now[pos - 1] = now[pos - 2] = 0; DFS(pos + 1, val + 1, r); now[pos] = now[pos - 1] = now[pos - 2] = 2;//恢复到递归前的状态 } if(pos && now[pos - 1] == 2 && last[pos - 1] == 2 && last[pos] == 2)//竖着放 { now[pos] = now[pos - 1] = 0; DFS(pos + 1, val + 1, r); now[pos] = now[pos - 1] = 2;//恢复到递归前的状态 } DFS(pos + 1, val, r);//不放 } int main () { scanf("%d", &T); while(T--) { scanf("%d%d%d", &n, &m, &k); lim = 1; cur = 0; for(int i = 1; i <= m; i++) lim *= 3; memset(mp, 0, sizeof(mp)); for(int i = 1; i <= k; i ++) { int x, y; scanf("%d%d", &x, &y); y--; mp[x][y] = 1; } memset(dp, -1, sizeof(dp)); dp[0][0] = 0; for(int i = 1; i <= n; i++) { cur ^= 1; memset(dp[cur], -1, sizeof(dp[cur])); for(int j = 0; j <= lim - 1; j++) if(dp[cur ^ 1][j] != -1) { Last = j; de_code(j); DFS(0, 0, i); } } int ans = 0; for(int j = 0; j <= lim - 1; j++) ans = max(ans, dp[cur][j]); printf("%d ", ans); } }