题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5266

2015年ACM/ICPC合肥赛区 D题(状压dp)

2015年ACM/ICPC合肥赛区 D题(状压dp)

题意:

给你一个n*n(n<=1000)的网格,初始第一行每一列有一只青蛙。

有n个膜,第个膜覆盖第l[i]列到第r[i]列。

每个膜只有一颗长生不老药。n个青蛙会一直往上跳,直到跳到第n行。如果一个膜覆盖所有列,则是好膜,否则是坏膜。

每个青蛙不能穿过超过10个坏膜,每个青蛙都要吃到长生不老药。求方法总数。

思路:状压dp。太难想了。。。

dp[i][j]表示第i列膜状态为j时的方法总数。

ne[i][j]=k表示第i行膜状态为j转移到第i+1行时膜状态为k。k=-1表示不合法。

初始:dp[1][1<<(第一列膜总数)-1]=1;

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2050;
const ll mo=105225319;
int n,m,k;
ll dp[maxn][maxn];
int ne[maxn][maxn];
vector<int>vc[maxn];
ll jc[maxn];
int l[maxn],r[maxn];
void init()
{
    jc[0]=1;
    for(ll i=1;i<maxn;i++)
    jc[i]=(jc[i-1]*i)%mo;
}
int find(int x,int y)
{
    for(int i=0;i<vc[x].size();i++)
    if(vc[x][i]==y) return i;
    return -1;
}
ll solve(int Q)
{
    memset(ne,0,sizeof(ne));
    for(int i=1;i<=n;i++)
    if(vc[i].size()>10) return 0;
    for(int i=1;i<n;i++)
    {
        int m1=vc[i].size(),m2=vc[i+1].size();
        for(int k=0;k<m2;k++)
        {
            int f=find(i,vc[i+1][k]);
            for(int s=0;s<(1<<m1);s++)
            {
                if(f==-1||(s&(1<<f)))
                ne[i][s]|=(1<<k);
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        int m1=vc[i].size();
        for(int s=0;s<(1<<m1);s++)
        {
            for(int j=0;j<m1;j++)
            if(s&(1<<j)){
                if(r[vc[i][j]]==i) ne[i][s]=-1;
            }
        }
    }
    memset(dp,0,sizeof(dp));
    dp[1][(1<<vc[1].size())-1]=1;
    for(int i=1;i<=n;i++)
    {
        int m1=vc[i].size();
        for(int j=0;j<(1<<m1);j++)
        {
            for(int k=0;k<m1;k++)
            if(j&(1<<k)){
                int zt=j^(1<<k);
                if(ne[i][zt]==-1) continue;
                dp[i+1][ne[i][zt]]=(dp[i+1][ne[i][zt]]+dp[i][j])%mo;
            }
        int zt=ne[i][j];
        if(zt!=-1) dp[i+1][zt]=(dp[i+1][zt]+dp[i][j])%mo;
        }
    }
    //cout<<jc[Q]<<" "<<dp[n+1][0]<<endl;
    ll ans=(jc[Q]*dp[n+1][0])%mo;
    return ans;
}
int main()
{
    init();
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&l[i]);
        for(int i=1;i<=n;i++) scanf("%d",&r[i]);
        for(int i=1;i<=n;i++) vc[i].clear();
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(l[i]==1&&r[i]==n) {cnt++;continue;}
            for(int j=l[i];j<=r[i];j++)
            {
                vc[j].push_back(i);
            }
        }
        printf("Case #%d: %lld\n",cas++,solve(cnt));
    }
    return 0;
}
/*
2
2
1 2
1 2
2
1 1
2 2
*/

 

相关文章: