HDU 5555 Immortality of Frog(状压DP)

Immortality of Frog

 

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 373    Accepted Submission(s): 131

 

 

Problem Description

N frogs are attempting to prolong their life-span. They live in the bottom of a well which can be described as a two-dimensional N×N grid. Grid(i,j) is located in the i−th row and the j−th column. At the beginning, the i−th frog lives in the bottom of i−th column, i.e. the place below grid(1,i).
The frogs are so devout that God decides to give them a chance. In each row i, a horizontal membrane ranging from (i,Li) to (i,Ri) inclusively is created. A capsule of elixir is placed at one of the grids of the membrane with uniform probability.

Now the frogs are jumping upwards to pursue immortality. The i−th frog would be in grid(j,i) after j jumps. When a frog arrives at a grid that contains a capsule of elixir, it will eat the capsule and gain immortality. After that, it continues jumping upwards until it gets out of the well.

A membrane is considered “bad” if it convers less than N grids. The frogs are very sensitive, so they can only endure passing through 10 bad membrane. When a frog reaches the 11th bad membrane, it thinks that there is no hope to get out of the well, so it will go back to the bottom of well and live there until death, even though it has eaten a capsule of elixir already.

The frogs are friends, so they want all of them gain immortality and live a happy life out of the well. They want to know the probability P that every frog eats exactly one capsule of elixir and gets out of the well.

 

 

Input

The first line of input contains a number T indicating the number of test cases (T≤100).

Each test case starts with a line containing an integer N as described above (1≤N≤1000). The second line contains N space separated integers L1,L2,...,LN. The third line contains N space separated integers R1,R2,...,RN. (1≤Li≤Ri≤N)

 

 

Output

For each test case, output a single line consisting of “Case #X: Y”. X is the test case number starting from 1. Y is an integer defined as the probability P multiplies ∏Ni=1(Ri−Li+1) . As the answer could be huge, you only need to output it module 105225319.

 

 

Sample Input

 

2 2 1 2 1 2 2 1 1 2 2

 

 

Sample Output

 

Case #1: 1 Case #2: 2

 

 

Source

2015ACM/ICPC亚洲区合肥站-重现赛(感谢中科大)

 

 

题意:

有一个N*N迷宫,每一行有一个横膜[L,R],每一个横膜上有一个长生药(位于横膜的某一个内(i,j)),现在有N只青蛙在井底(0,i)(1<=i<=n),青蛙只能沿着自己的列i向上跳去追求长生药,拿到长生药的青蛙就会继续向上跳出井,没拿到的就只能返回井底。并且横膜的长度为N的话称为好膜,<N的话称为坏膜,如果青蛙经过某一行被膜覆盖的部分且这个膜是坏膜,心情就会变差。当踩上第11个坏膜时,青蛙会忍受到极限,即使有长生药的情况下,也会跳回井底。问有多少种药的放法,使得所有的青蛙都能跳出井

 

 

解析:

这里佷显然可以知道如果存在一列坏膜的数量>10个的话,所有青蛙都不可能逃生,ans=0

然后每一列坏膜数<10的话,那么我们就可以用状压DP来做   这里可以先看一个博客点击打开链接

 

这里是对列进行dp,并且状压的对象是坏膜(因为坏膜的数量最多只有10个)

DP[i][j]第i列状态为j的方案数,j表示覆盖第i列的坏膜的在1-i列的状态

dp[i+1][j']是由第i+1列不放药的+第i+1列放药的得来,

反过来,就是dp[i][now](now为i列状态j变换成i+1列状态now)要为dp[i+1][now](不放药)和dp[i+1][(now|(1<<k))](在相对位置为k的坏膜放药)贡献答案

 

关键就是第i列的状态转向第i+1列的状态时,我们对于同时覆盖i列和i+1列的坏膜的状态需要继承下去(其他的状态就不需要了),并且在第i列停止的坏膜我们需要保证他一定有一个药(合法)。然后我们就可以通过第i+1列的药在好膜上(即坏膜上不拿药)+第i+1列的药在坏膜上的情况。

 

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int lli;
#define MOD 105225319

const int MAXN = 1e3+100;
const int NN=(1<<10)+100;   //!

int LL[MAXN],RR[MAXN];
vector<int> g[MAXN];

int col1[20],col2[20];

lli dp[MAXN][NN];
int n;

void pre_treated(int x)
{
    memset(col1,-1,sizeof(col1));
    memset(col2,-1,sizeof(col2));
    for(int i=0;i<g[x].size();i++)
    {
        for(int j=0;j<g[x+1].size();j++)
        {
            if(g[x][i]==g[x+1][j]) col1[i]=j;
        }
    }

    for(int i=0;i<g[x+1].size();i++)
    {
        for(int j=0;j<g[x].size();j++)
        {
            if(g[x+1][i]==g[x][j]) col2[i]=j;
        }
    }
}

int _transform(int x,int y)   //将第i列的状态转换成i+1列的状态
{
    int res=0;
    for(int i=0;i<g[x].size();i++)
    {
        if(col1[i]==-1)
        {
            if((y&(1<<(i)))==0) return -1;  //非法情况,不加入计算
        }
        else
        {
            if((y&(1<<(i)))) res|=(1<<col1[i]);

        }

    }
    return res;
}

int main()
{
    int t;
    int ncount=0;
    int good;
    scanf("%d",&t);
    while(t--)
    {
        good=0;
        ncount++;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&LL[i]);
        }

        for(int i=1;i<=n;i++)
        {
            scanf("%d",&RR[i]);
            g[i].clear();
        }
        int maxcol=-1;
        for(int i=1;i<=n;i++)   //预处理出每列坏膜的位置,用列来做DP
        {
            if(RR[i]-LL[i]+1==n)
            {
                good++;
                continue;
            }
            maxcol=max(maxcol,i);
            for(int j=LL[i];j<=RR[i];j++)
            {
                g[j].push_back(i);
            }
        }
        int flag=0;
        for(int i=1;i<=n;i++)
        {
            if(g[i].size()>10)
            {
                flag=1;
                break;
            }
        }
        printf("Case #%d: ",ncount);
        if(flag)
        {
            printf("0\n");
        }
        else
        {
            memset(dp,0,sizeof(dp));
            lli ans=0;
            dp[0][0]=1;
            for(int i=0;i<n;i++)  //第i列
            {
                pre_treated(i);
                for(int j=0;j<(1<<(g[i].size()));j++)   //j=0的情况是表示第i列没有药的情况
                {
                    int now=_transform(i,j);
                    if(now==-1) continue; //dp[i][j]中j是i列中存在的膜在1-i列的状态值,所以在1-i列可能存在非法的情况,但我们计算了,now==-1就是为了不让非法的情况
					//加到后面的情况去
                    dp[i+1][now]=(dp[i+1][now]+dp[i][j])%MOD;   //表示i+1列没有药,将药都放在前面i列的情况加到对应的state里
                    //printf("dp[%d][%d]=%lld\n",i,j,dp[i][j]);
                    for(int k=0;k<g[i+1].size();k++)  //第x+1列的青蛙吃了相对位置为k的坏膜中的药(即k位置坏膜的药在第x+1列)
                    {
                        if(col2[k]==-1||(now&(1<<k))==0) //!(now>>k&1)保证了一行最多只有一个
                        {
                            dp[i+1][(now|(1<<k))]=(dp[i+1][(now|(1<<k))]+dp[i][j])%MOD;   //将第i列的状态转换成第i+1列的(now|(1<<k))
                        }
                    }
                }
            }
            ans=dp[n][((1<<g[n].size())-1)]%MOD;//前面所有的情况(即使某些行在最后一列没有膜)都加入到最后一行上了,这样我们只要保证膜在最后一列的那些膜有药就行了
            for(int i=good;i>1;i--)
            {
                ans=(ans*i)%MOD;
            }
            printf("%lld\n",ans);


        }


    }
    return 0;
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值