Live2D

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]);
}

 

posted @ 2018-11-07 22:30  ztz11  阅读(222)  评论(0编辑  收藏  举报