题目
有一些条件,首先四个选项的数量必须分别为 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 <bits/stdc++.h>
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];
vector<int>e[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;
}