yyy loves Maths VII(状压DP)
题目背景
yyy对某些数字有着情有独钟的喜爱,他叫他们为幸运数字;然而他作死太多,所以把自己讨厌的数字成为"厄运数字"
题目描述
一群同学在和yyy玩一个游戏
每次,他们会给yyy n张卡片,卡片上有数字,所有的数字都是"幸运数字",我们认为第i张卡片上数字是ai
每次yyy可以选择向前走ai步并且丢掉第i张卡片
当他手上没有卡片的时候他就赢了
但是呢,大家对"厄运数字"的位置布置下了陷阱,如果yyy停在这个格子上,那么他就输了
(注意:即使到了终点,但是这个位置是厄运数字,那么也输了)
现在,有些同学开始问:
yyy有多大的概率会赢呢?
大家觉得这是个好问题
有人立即让yyy写个程序
"电脑运行速度很快!24的阶乘也不过就620448401733239439360000,yyy你快写个程序来算一算"
yyy表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是%1,000,000,007以后的值
大家都不会写程序,只好妥协
但是这时候yyy为难了,24!太大了,要跑好长时间.
他时间严重不够!需要你的帮助!
由于yyy人格分裂,某个数字可能既属于幸运数字又属于厄运数字。
思路:
想起来不难,就是写起来超级麻烦(卡常能把你卡疯)
基本的状压大家都会,就不加以介绍了
主要介绍一种优化
我们以往状压的时候是要枚举丢了哪张卡牌
但显然,O(n)扫一遍的话我们可能丢掉本来就已经丢掉的卡牌
我们可以使用lowbit优化
每次lowbit出一个这一时刻被扔掉的纸牌即可
可以大大优化常数
加上register就过了
代码:
#include<stdio.h> #define p 1000000007 #define rii register int i #define rij register int j int dp[1<<24],ycl[1<<24],n,m,x[3]; int main() { scanf("%d",&n); for(rii=0;i<n;++i) { scanf("%d",&ycl[1<<i]); } scanf("%d",&m); for(rii=1;i<=m;++i) { scanf("%d",&x[i]); } dp[0]=1; register int bj=1<<n; for(rii=1;i<bj;++i) { register int low,yi; low=i; yi=i&(-i); ycl[i]=ycl[i^yi]+ycl[yi]; if(ycl[i]==x[1]||ycl[i]==x[2]) { continue; } while(low!=0) { yi=low&(-low); dp[i]+=dp[i^yi]; if(dp[i]>p) { dp[i]-=p; } low^=yi; } } printf("%d",dp[bj-1]); }