Redistributing Gifts(状压dp)

题目
题意:给定 n ( n < = 18 ) n(n<=18) n(n<=18)头奶牛,对应有 n n n种小麦。每个奶牛对于小麦有自己特定的喜好排列。奶牛有两种H,或G。一开始,奶牛 i i i拥有小麦 i i i
现在对小麦重新分配,要求

  • 分配后,每只奶牛得到的小麦,喜好值不能下降。
  • 同时,相同类型的奶牛,才能进行小麦的重新分配,即分配前的小麦所属奶牛,和分配后的小麦所属奶牛,应该要属于同一种类型。

Q ( Q < = 2 N ) Q(Q<=2^N) Q(Q<=2N)次查询,每次查询给出每只奶牛的类型,求对应场景下的,分配方案数有多少种。

官方题解
思路:定义 d p [ m a s k ] [ l a s t ] dp[mask][last] dp[mask][last](其中mask最高位为 i i i)表示除了小麦 i i i和奶牛 l a s t last last,小麦集 m a s k ⨁ i mask\bigoplus i maski已经得到配对,奶牛集 m a s k ⨁ l a s t mask\bigoplus last masklast已经得到配对,的方案数。
得到匹配是指,匹配的小麦(奶牛)集中任意一个小麦(奶牛),可以在奶牛(小麦)集中找到能吃它(被吃)的。

dp转移思路,对于当前的 d p [ m a s k ] [ l a s t ] dp[mask][last] dp[mask][last]

如果奶牛 l a s t last last中,存在可以吃的小麦 k ( k < i ) k(k<i) k(k<i)(即小麦 k k k的喜好值大于等于奶牛 l a s t last last原先吃的小麦),且 k k k还没有在小麦集 l a s t ⨁ i last\bigoplus i lasti中。那么奶牛 l a s t last last可以选择吃掉小麦。那么此时没配对的奶牛,则转移为奶牛 k k k,掩码转移为 m a s k ( 1 < < k ) mask^(1<<k) mask(1<<k)
此时 d p [ m a s k t ⨁ ( 1 < < k ) ] [ k ] + = d p [ m a s k ] [ l a s t ] dp[maskt\bigoplus (1<<k)][k]+=dp[mask][last] dp[maskt(1<<k)][k]+=dp[mask][last]

如果奶牛 l a s t last last可以和小麦 i i i匹配,那么此时 m a s k mask mask则闭环了,即小麦集 m a s k mask mask和奶牛集 m a s k mask mask都得到了匹配了
此时 a n s [ m a s k ] + = d p [ m a s k ] [ l a s t ] ans[mask]+=dp[mask][last] ans[mask]+=dp[mask][last]

dp和ans初始化: d p [ 1 < < k ] [ k ] = 1 , a n s [ 0 ] = 1 dp[1<<k][k]=1,ans[0]=1 dp[1<<k][k]=1,ans[0]=1

求完每个 a n s [ m a s k ] ans[mask] ans[mask]时,记得更新最高位大于i的k,对应的dp状态。
d p [ m a s k ⨁ ( 1 < < k ) ] [ k ] + = a n s [ m a s k ] dp[mask\bigoplus (1<<k)][k]+=ans[mask] dp[mask(1<<k)][k]+=ans[mask]

对于每次查询,我们计算两种种类奶牛的mask1,mask2,方案数为 a n s [ m a s k 1 ] ∗ a n s [ m a s k 2 ] ans[mask1]*ans[mask2] ans[mask1]ans[mask2]

挺难想到的,脑壳疼>.<

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 20;

ll dp[1<<N][N];
ll ans[1<<N];
string op;
int n, x, g[N];
int main() {
	cin >> n;
	for (int i = 0; i < n; ++i) {
		bool flag = true;
		for (int j = 0; j < n; ++j) {
			cin >> x;
			--x;
			if (!flag) {
				continue;
			}
			g[i] ^= (1 << x);// 存储所有奶牛i能吃的小麦x
			if (x == i) {
				flag = false;
			}
		}
	}
	
	ans[0] = 1;
	for (int k = 0; k < n; ++k) {
		dp[1<<k][k] = 1;
	}
	
	for (int i = 0; i < n; ++i) {
		for (int mask = (1 << i); mask < (1 << (i + 1)); ++mask) {
			for (int last = 0; last <= i; ++last) {
				// 过滤不在当前集合的奶牛 
				if (!(mask & (1 << last))) {
					continue;
				}
				ll val = dp[mask][last];
				for (int k = 0; k < i; ++k) {
					// 不在mask中且奶牛可以吃的小麦k,进行状态转移 
					if (!(mask & (1 << k)) && (g[last] & (1 << k))) {
						dp[mask^(1<<k)][k] += val;
					}
				}
				// 如果奶牛last可以吃小麦i,则mask闭环了 
				if (g[last] & (1 << i)) {
					ans[mask] += val;
				}
			}
			// 计算完ans[mask] 更新高位mask的dp 
			for (int k = i + 1; k < n; ++k) {
				dp[mask^(1<<k)][k] += ans[mask];
			}
		}
	}
	
	int q;
	cin >> q;
	while (q--) {
		cin >> op;
		int h = 0, y = 0;
		for (int i = 0; i < n; ++i) {
			if (op[i] == 'H') {
				h ^= (1 << i);
			} else {
				y ^= (1 << i);
			}
		}
		cout << ans[h] * ans[y] << endl;
	}
}
/*

*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值