题目描述
众所周知,高考数学中有一个题目是给出12个单项选择,每一个选择的答案是 A,B,C,D 中的一个。
网上盛传答案存在某种规律,使得蒙对的可能性大大增加。于是今年老师想让你安排这12个题的答案。但是他有一些条件,首先四个选项的数量必须分别为 na,nb,nc,nd。其次有 m 个额外条件,分别给出两个数字 x,y,代表第 x 个题和第 y 个题的答案相同。 现在你的老师想知道,有多少种可行的方案安排答案。
输入描述:
第一行五个非负整数na,nb,nc,nd,m,保证na+nb+nc+nd=12,0≤m≤1000。
接下来m行每行两个整数x,y(1≤ x,y ≤12)代表第x个题和第y个题答案必须一样。
输出描述:
仅一行一个整数,代表可行的方案数。
题解:
我们按如图所示,把答案相同的放在一个树上,把题目个数往根节点转移。那么到时我们只需要考虑每个树的根节点即可,大大减小了时间复杂度。过程代码中分析;
#include<iostream>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=20;
int tree[N];
int a[5];
int head[N];
int cnt;
int gethead(int x)
{return x==head[x]?x:head[x]=gethead(head[x]);
}
ll ans=0;
void bfs(int x)
{
if(x==cnt+1){
ans++;
return;
}
for(int i=1;i<=4;i++)
if(a[i]>=tree[x])//遍历找情况
{
a[i]-=tree[x];
bfs(x+1);
a[i]+=tree[x];
}
}
int main()
{
int m;
cin>>a[1]>>a[2]>>a[3]>>a[4]>>m;
int x,y;
for(int i=1;i<=12;i++)
head[i]=i,tree[i]=1;//最初的时候每个节点权值都为一,故自己就是自己的根节点;
while(m--)
{cin>>x>>y;
head[x]=gethead(x);//找到父节点,这里不懂的可以找点博客看看建树,我不做过多解释
head[y]=gethead(y);
head[head[y]]=head[x];//让y父节点的父节点等于x的父节点
}
for(int i=1;i<=12;i++)
if(gethead(i)==i)cnt++;
else tree[head[i]]++;
sort(tree+1,tree+14,greater<int>());//从大到小,把根节点换到前面
sort(a+1,a+1+4,greater<int>());//根节点从大到小,a也要从大到小
bfs(1);
cout<<ans<<endl;
}