HDU 6407 Pop the Balloons (状压dp + 剪枝 。。。 结果大数 int128就行 )

HDU 6407 Pop the Balloons (状压dp + 剪枝 。。。 结果大数 int128就行 )_第1张图片

 HDU 6407 Pop the Balloons (状压dp + 剪枝 。。。 结果大数 int128就行 )_第2张图片

 HDU 6407 Pop the Balloons (状压dp + 剪枝 。。。 结果大数 int128就行 )_第3张图片

具体 看代码吧:

#include 
using namespace std;

#ifdef __LOCAL_DEBUG__
# define _debug(fmt, ...) fprintf(stderr, "\033[94m%s: " fmt "\n\033[0m", \
    __func__, ##__VA_ARGS__)
#else
# define _debug(...) ((void) 0)
#endif
#define rep(i, n) for (int i=0; i<(n); i++)
#define Rep(i, n) for (int i=1; i<=(n); i++)
#define range(x) (x).begin(), (x).end()
typedef long long LL;
typedef unsigned long long ULL;

constexpr int MAXM = 12;
LL dp[32][1< 0) {
    if (dig[pos]) break;
    pos--;
  }
  printf("%d", dig[pos]);
  while (pos > 0) {
    pos--;
    printf("%09d", dig[pos]);
  }
  puts("");
#endif
}

int main()
{
	int T;
	scanf("%d", &T);
	fact[0] = 1;
	Rep (i, 20) fact[i] = fact[i-1] * i;
	while (T--)
	{
	    //输入
		scanf("%d%d%d", &m, &n, &k);
		rep (i, m) scanf("%s", g[i]);
		memset(a, 0, sizeof a);
		Rep (j, n) rep (i, m)
		{
		    //将每列转为二进制  气球为1
	 		a[j] = a[j] << 1 | (g[i][j-1] == 'Q');
		}
		memset(ans, 0, sizeof ans);
		//枚举所有 想要扎爆的行的状态为fin   dp[r][mask] 表示 前r行 已经扎爆的行为mask的情况
		for (unsigned fin = 1; fin < (1u << m); fin++)
		{
		    //一开始都为0 除了什么都没有的时候  其他的清零在下面做了
		  	dp[0][0] = 1;
		  	//遍历每一列   
		  	Rep (r, n)
			{
		        bool flag = false;
		        //遍历每一个mask   mask代表的扎爆的行 一定是fin里想要扎爆的行  mask = (mask - 1) & fin手模下就会发现 位运算真秀
		        for (unsigned mask = fin; ; mask = (mask - 1) & fin)
				{
				    //只考虑这种状态存在的情况  剪枝
		          	if (dp[r - 1][mask])
			  		{
			  		    //如果  第r列的气球 都在我想要扎爆的行上  我可以选择不在第r列扎爆某个气球(不扎爆某个行)并打上标记表示这列气球我可能扎完
		            	if ((a[r] & fin) == a[r])
						{
		            	  if (dp[r][mask] += dp[r-1][mask]) flag = true;
		 		 		}
		 		 		// dif &= dif - 1 是删去二进制里最右边的1 bit = dif & -dif 是取得最右边1的位置
		 		 		//这里我们找  想要扎爆的 但是还没扎爆的行 并且 与第r列的气球 中有行相同的(如果没有行相同的就说明我不能把这列气球扎完 这种情况不可取)
		            	for (unsigned dif = (fin ^ mask) & a[r]; dif; dif &= dif - 1)
						{
				          unsigned bit = dif & -dif;
				          //更新 选第r列某一行的气球扎爆后的方案数
				          dp[r][mask | bit] += dp[r-1][mask];
				          flag = true;
				 		}
                    // 更新完第r列清空第r-1列
		            dp[r-1][mask] = 0;
		          }
		          if (mask == 0) break;
		        }
		        // 不能扎完气球  所以跳出
		        if (flag == false) break;
		  	}
		  	//fin的二进制中1的个数x 表示我们扎了x个气球  更新答案
		  ans[__builtin_popcount(fin)] += dp[n][fin];
		  //清0
		  for (unsigned mask = fin; ; mask = (mask - 1) & fin) {
		    dp[n][mask] = 0;
		    if (mask == 0) break;
		  }
		}
		//输出要乘与阶乘 所以用大数乘法
		Rep (i, k) print64mul(ans[i], fact[i]);
	}
	return 0;
}

 

你可能感兴趣的:(动态规划)