【cdoj 1335】柱爷与三叉戟不得不说的故事 状压dp+子集枚举

其实拿到这道题很容易就能想到是状压dp,原因很简单,因为他每一次打开一个洞穴以后就必须要拿走里面所有的元素(那个伤害什么老大爷就是纯属扯淡),就类似与一个打包子集的概念,而题目要求必须得到所有的元素,再加上元素总共也不多就15个,联想一下最经常做的状压dp的数据范围很容易就想到了

但是今天把枚举子集忘了,还翻了一下大白90页的黑客那道题才记起来:

对于一个全集 S0,他的子集S就是不断地用 (S-1)&S0 来更新,知道最后为0.

原理:S0&(S - 1) 实际上是把S中的0全部忽略,并不断减1的结果。

例如一个二进制(只是例如没有实际验证计算数字的正确性,懂那个意思就好了)

1000101 不断更新就会是1000100 1000001 1000000.... 1不断减少

#include
#include
#include
#define ll long long 
using namespace std;
ll pos[1500000];


int main(){
	ll x,n,y,z;
	for(int i=1;i<=1e6;i++)pos[i]=1e17+7;
	for(ll i=1;i<=15;i++){
		scanf("%lld",&x);
		pos[1<<(i-1)]=x;
	}
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++){
		scanf("%lld",&x);
		y=0;
		for(ll i=1;i<=x;i++){
			scanf("%lld",&z);
			y|=1<<(z-1);
		}
		scanf("%lld",&x);
		if(pos[y]>x)pos[y]=x;
	}
	for(ll i=1;i<(1<<15);i++){
		for(ll j=i;j>0;j=(j-1)&i){
			pos[i]=min(pos[i],pos[j]+pos[j^i]);
		}
	}
	printf("%lld",pos[(1<<15)-1]);
	return 0;
}


你可能感兴趣的:(ac之路)