牛客练习赛61-c题 四个选项

题目链接

题目描述
众所周知,高考数学中有一个题目是给出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个题答案必须一样。
输出描述:
仅一行一个整数,代表可行的方案数。

题解:牛客练习赛61-c题 四个选项_第1张图片
我们按如图所示,把答案相同的放在一个树上,把题目个数往根节点转移。那么到时我们只需要考虑每个树的根节点即可,大大减小了时间复杂度。过程代码中分析;

#include
#include
#include
#include
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;

}

你可能感兴趣的:(树)