【题解】洛谷P2396 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人格分裂,某个数字可能既属于幸运数字又属于厄运数字。

输入输出格式

输入格式:
第一行n

下面一行n张卡片

第三行m 表示yyy的厄运数字个数(最多2个)

最后一行是m个厄运数字

输出格式:
方案数%1,000,000,007

输入输出样例

输入样例#1:
8
1 3 1 5 2 2 2 3
0
输出样例#1:
40320
输入样例#2:
24
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2
10 15
输出样例#2:
0

说明

数据范围:

10%的数据n<=10

50%的数据n<=23

100%的数据n<=24


写此题时间同样很久远,但一下子就勾起我被卡常的痛苦回忆了

#include<cstdio>
//typedef long long ll;写成long long 就挂了 
const int mod=1e9+7;
int /*jc[25],*/dp[1<<24],dis[1<<24],ey[2];//开大了 
//jc[i]表示i的阶乘、dis[i]表示状态为i时走的距离,dp[i]表示状态i对应方案数 
/*inline void Init()//不要了 
{
    int now=1;
    for(int i=1;i<=24;i++)
    {
        now=now*i%mod;
        jc[i]=now;
    }
}*/
/*inline int lowbit(int x)
{
    return x&(-x);
}*/
int main()
{
    #ifdef local
    freopen("in.txt","r",stdin);
    #endif
    int i,j,k,n,m;
    //Init();
    scanf("%d",&n);
    for(i=0;i<n;i++)
    scanf("%d",&dis[1<<i]);
    scanf("%d",&m);
    if(m==1)scanf("%d",&ey[0]);
    else scanf("%d%d",&ey[0],&ey[1]);
    dp[0]=1;
    for(i=1;i<=(1<<n)-1;i++)//此处写成1<<(n-1) 
    {
        j=i&(-i);k=i;
        dis[i]=dis[j]+dis[i^j];//j状态距离和去掉j状态距离
        if(dis[i]==ey[0]||dis[i]==ey[1])continue;//非法
        while(k)
        {
            j=k&(-k);
            dp[i]+=dp[i^j];//去掉j状态对应方案数
            if(dp[i]>=mod)dp[i]-=mod;
            k^=j;//去掉j状态 
        } 
    }
    printf("%d\n",dp[(1<<n)-1]);//此处写成dp[n] 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值