插头DP

hdoj 1693 Eat the Trees

解题报告详见:http://hi.baidu.com/fqq11679/blog/item/423bcd4a3d956bf983025c6d.html

考虑本题规模较小,处理的方式是逐格递推的状态压缩DP。


如上图,我们用F[i,j,k]表示轮廓线在i行j列出外凸,轮廓线状态为k的方案数。
这里k是一个c+1位二进制数,1表示轮廓线第i个单元有插头,0表示没有。
那么转移就考虑两种,第一种是换行,这个就要把k先右移一位,然后把k
的前两位转移成凸起,更新F[i+1,1,k']。
第二种就是直接右推一格,这个就是直接在k中找出凹角对应的两位数,转换
得到新状态k'然后更新F[i,j+1,k']。
至于k如何转k'只需考虑凹角的几种情况:

1.凹角为11(横竖都有插头):新状态k'的这两位均为0,因为两个度用完了。
2.凹角为01(只有横边有插头):新状态k'可以是01或者10。
3.凹角为10(只有竖边有插头):同上。
4.凹角为00(都没插头):要么11,要么00。(00的条件是此格为障碍)

这样Dp一下,最后的答案就是F[r][c][0]。

#include <iostream>
#include <cstring>
using namespace std;

typedef __int64 lld;

int n, m, ca = 1;
int mp[12][12];
lld dp[12][12][1<<12];



void solve() {
	int i, j, k, x, y, p, q, v, len1;
	memset( dp, 0, sizeof(dp) );
	dp[0][m][0] = 1;
	len1 = 1<<(m+1);
	for( i=0; i<=n; ++i ) {
		for( j=1; j<=m; ++j ) {
			if( i == 0 && j < m ) continue;
			for( k=0; k<len1; ++k ) {
				if( j == m ) {//换行递推
					if( i < n ) {
						v = k >> 1;
						p = 1 << m;
						q = 1 << (m - 1);
						x = v & p;
						y = v & q;
						if( x == 0 && y == 0 ) {
							if( mp[i+1][1] ) {
								if( m > 1 ) dp[i+1][1][v-x-y+p+q] += dp[i][j][k];
							} else {
								dp[i+1][1][v-x-y] += dp[i][j][k];
							}
								
						}else if( x == 0 && y != 0 ) {
							if( mp[i+1][1] ) {
								if( m > 1 ) dp[i+1][1][v-x-y+q] += dp[i][j][k];
								dp[i+1][1][v-x-y+p] += dp[i][j][k];
							}
						}
					}

				} else {//向右递推
					v = k;
					p = 1 << (m - j);
					q = 1 << (m - j - 1);
					x = v & p;
					y = v & q;
                    if( x == 0 && y == 0 ) {
						if( mp[i][j+1] ) {
							if( j < m-1 ) dp[i][j+1][v-x-y+p+q] += dp[i][j][k];
						} else {
							dp[i][j+1][v-x-y] += dp[i][j][k];
						}
					} else if ( x != 0 && y != 0 ) {
						if( mp[i][j+1] ) dp[i][j+1][v-x-y] += dp[i][j][k];
					} else {
						if( mp[i][j+1] ) {
							if( j < m-1 ) dp[i][j+1][v-x-y+q] += dp[i][j][k];
							dp[i][j+1][v-x-y+p] += dp[i][j][k];
						}
					}

				}

			}
		}
	}

	printf( "Case %d: There are %I64d ways to eat the trees.\n", ca++, dp[n][m][0] );
}






int main() {
//	freopen("c:/aaa.txt", "r", stdin);
	int T, i, j;
	scanf( "%d", &T );
	while( T-- ) {
		scanf( "%d %d", &n, &m );
		for( i=1; i<=n; ++i ) {
			for( j=1; j<=m; ++j ) {
				scanf( "%d", &mp[i][j] );
			}
		}
		solve();
	}
	return 0;
}

posted on 2011-02-27 09:51  CrazyAC  阅读(5480)  评论(0编辑  收藏  举报