牛客練習賽61 C 四個選項 dp\搜索+組合數學

https://ac.nowcoder.com/acm/contest/5026/C
在這裏插入圖片描述思路一:dpdp,首先通過並查集處理出所有的連通塊(限制條件),然後用dpi,x,y,z,wdp_{i,x,y,z,w}表示處理第ii個連通塊時,選了xxAAyyBBzzCCwwDD時的方案數,枚舉轉移即可,詳見代碼。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

int n,m;
int a[4],f[15],vis[15],idx[15],cnt[15];
ll dp[13][13][13][13][13];

int father(int x)
{
    return f[x]==x?x:f[x]=father(f[x]);
}

int main()
{
    for(int i=1;i<=12;i++)
        f[i]=i;
    for(int i=0;i<4;i++)
        scanf("%d",&a[i]);
    scanf("%d",&m);
    int x,y,fx,fy;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        fx=father(x),fy=father(y);
        if(fx!=fy)
            f[fx]=fy;
    }
    int id=0,idx;
    for(int i=1;i<=12;i++)
    {
        fx=father(i);
        if(!vis[fx])
            vis[fx]=++id;
        idx=vis[fx];
        ++cnt[idx];
    }
    dp[0][0][0][0][0]=1;
    for(int i=1;i<=id;i++)
    {
        for(int x=0;x<=a[0];x++)
        {
            for(int y=0;y<=a[1];y++)
            {
                for(int z=0;z<=a[2];z++)
                {
                    for(int w=0;w<=a[3];w++)
                    {
                        if(dp[i-1][x][y][z][w])
                        {
                            if(x+cnt[i]<=a[0])
                                dp[i][x+cnt[i]][y][z][w]+=dp[i-1][x][y][z][w];
                            if(y+cnt[i]<=a[1])
                                dp[i][x][y+cnt[i]][z][w]+=dp[i-1][x][y][z][w];
                            if(z+cnt[i]<=a[2])
                                dp[i][x][y][z+cnt[i]][w]+=dp[i-1][x][y][z][w];
                            if(w+cnt[i]<=a[3])
                                dp[i][x][y][z][w+cnt[i]]+=dp[i-1][x][y][z][w];
                        }
                    }
                }
            }
        }
    }
    printf("%lld\n",dp[id][a[0]][a[1]][a[2]][a[3]]);
    return 0;
}

思路二:爆搜+組合,假設在處理完所有的限制條件之後,ABCDA、B、C、D的數量還剩下abcda、b、c、d個,不妨設sum=a+b+c+dsum=a+b+c+d,那麼此時的方案數量就等於C(sum,a)C(suma,b)C(sumab,c)C(sumabc,d)C(sum,a)*C(sum-a,b)*C(sum-a-b,c)*C(sum-a-b-c,d)。那麼預處理出所有的連通塊後,刪去僅有11個元素的連通塊(相當於該位置沒有限制條件)後,爆搜處理即可。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

int n,m,num;
ll ans;
int a[4],f[15],vis[15],idx[15],cnt[15];
ll C[15][15];

int father(int x)
{
    return f[x]==x?x:f[x]=father(f[x]);
}

void dfs(int j,int len)
{
    if(j>len)
    {
        int s0=a[0],s1=s0+a[1],s2=s1+a[2];
        ans+=C[num][a[0]]*C[num-s0][a[1]]*C[num-s1][a[2]]*C[num-s2][a[3]];
        return ;
    }
    for(int i=0;i<4;i++)
    {
        if(a[i]>=cnt[j])
        {
            a[i]-=cnt[j];
            dfs(j+1,len);
            a[i]+=cnt[j];
        }
    }
}

int main()
{
    for(int i=0;i<=12;i++)
        C[i][0]=1;
    C[1][1]=1;
    for(int i=2;i<=12;i++)
        for(int j=1;j<=i;j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
    for(int i=1;i<=12;i++)
        f[i]=i;
    for(int i=0;i<4;i++)
        scanf("%d",&a[i]);
    scanf("%d",&m);
    int x,y,fx,fy;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        fx=father(x),fy=father(y);
        if(fx!=fy)
            f[fx]=fy;
    }
    int id=0,idx;
    num=12;
    for(int i=1;i<=12;i++)
    {
        fx=father(i);
        if(!vis[fx])
            vis[fx]=++id;
        idx=vis[fx];
        ++cnt[idx];
        --num;
    }
    sort(cnt+1,cnt+1+id,greater<int>());
    while(cnt[id]==1)
        --id,++num;
    dfs(1,id);
    printf("%lld\n",ans);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章