牛客练习赛61 C.四个选项(01背包+哈希压状态)

题目

有一些条件,首先四个选项的数量必须分别为 na,nb,nc,nd(保证na+nb+nc+nd=12)。

其次有 m(0<=m<=1e3) 个额外条件,分别给出两个数字 x,y,代表第 x 个题和第 y 个题的答案相同(1<=x,y<=12)。

现在你的老师想知道,有多少种可行的方案安排答案。

思路来源

https://ac.nowcoder.com/discuss/405216?type=101&order=0&pos=1&page=1

题解

dp[i][x1][x2][x3][x4]为枚举到第i个物品,四个背包被装填的体积分别为x1,x2,x3,x4的方案数,直接枚举填哪个背包转移即可。

由于数据量较小,dfs也是可以的。

如果数据量再大一点,可以把所有背包的体积的所有状态哈希一下,变成一个二维dp,再滚动一下第一维即可。


“比方说四个背包的体积和是200, 你总不能开[200][200][200][200]这样的数组吧,即使滚动一维代价也好大,

但是所有可能的元组(a,b,c,d)至多50*50*50*50个,所以可以把这些元组哈希一下。”

 

dfs做法显然,本题试一下哈希压状态的写法,之前没这么搞过。

代码

#include 
using namespace std;
const int N=4;
int m,x,y;
int dp[N*N*N*N],a[13],tot,cnt,now[4],b[4],nex,pre;
bool vis[13];
vectore[13];
void dfs(int u){
    cnt++;
    vis[u]=1;
    for(auto &v:e[u]){
        if(!vis[v])dfs(v);
    }
}
int g(int a[4]){
    int ans=0;
    for(int i=0;i<4;++i){
        ans=ans*(now[i]+1)+a[i];
    }
    return ans;
}
int main(){
    for(int i=0;i<4;++i){
        scanf("%d",&now[i]);
    }
    scanf("%d",&m);
    while(m--){
        scanf("%d%d",&x,&y);
        e[x].push_back(y);
        e[y].push_back(x);
    }
    for(int i=1;i<=12;++i){
        if(!vis[i]){
            cnt=0;
            dfs(i);
            a[++tot]=cnt;
        }
    }
    dp[0]=1;
    int cal=0;
    for(int i=1;i<=tot;++i){
        for(int j=now[0];j>=0;j--){
            for(int k=now[1];k>=0;k--){
                for(int l=now[2];l>=0;l--){
                    for(int m=now[3];m>=0;m--){
                        b[0]=j;b[1]=k;b[2]=l;b[3]=m;
                        nex=g(b);
                        if(b[0]>=a[i])b[0]-=a[i],pre=g(b),dp[nex]+=dp[pre],b[0]+=a[i];
                        if(b[1]>=a[i])b[1]-=a[i],pre=g(b),dp[nex]+=dp[pre],b[1]+=a[i];
                        if(b[2]>=a[i])b[2]-=a[i],pre=g(b),dp[nex]+=dp[pre],b[2]+=a[i];
                        if(b[3]>=a[i])b[3]-=a[i],pre=g(b),dp[nex]+=dp[pre],b[3]+=a[i];
                    }
                }
            }
        }
    }
    printf("%d\n",dp[g(now)]);
    return 0;
}

 

你可能感兴趣的:(#,背包九讲)