欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-01 11:27:31
...

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

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

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

具体 看代码吧:

#include <bits/stdc++.h>
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<<MAXM], fact[32];
LL ans[32];
unsigned a[32];
char g[32][32];
int rcnt[32];
int m, n, k;

void print64mul(LL x, LL y) {
#if __SIZEOF_INT128__ == 16
  __int128 res = __int128(x) * y;
  char ch[64];
  int ptr = 0;
  if (res == 0) {
    puts("0");
    return;
  }
  while (res) {
    ch[ptr] = (res % 10) + '0';
    ptr++;
    res /= 10;
  }
  while (ptr)
    putchar(ch[--ptr]);
  putchar('\n');
#else
  constexpr LL limit = 1000000000;
  LL dig[4] = {0};
  dig[0] = x % limit;
  dig[1] = x / limit;
  dig[0] *= y;
  dig[1] *= y;
  dig[1] += dig[0] / limit;
  dig[0] %= limit;
  dig[2] += dig[1] / limit;
  dig[1] %= limit;
  int pos = 2;
  while (pos > 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;
}