題目
有一些條件,首先四個選項的數量必須分別爲 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;
}