(Ramsey定理 )(鸽巢定理)( 组合数学)

组合数学

鸽巢原理典型题目:
吃糖果:
很奇妙的思路找糖果最多的作为隔板,假设有N个隔板,剩余的糖果S必须有S>=N才有唯一解(可以分类讨论一下很有趣,在S<N时,无解)

鸽巢原理是Ramsey定理的一个特例,下面是Ramsey的知识:
Ramsey定理:世界上任意3个人之间,总有3个人互相认识或3个人互相不认识

证明:假设6个人,a,b,c,d,e,f,有a出发有,ab,ac,ad,ae,af,这五个线段,他们之间认识用红色标记,不认识用蓝色标记。
由抽屉定理得,这5条线段中至少有3个同色。不妨设ab,ac,ad为红色,讨论b,c,d三者的情况,若bc,bd,cd三者至少一个为红色,则这6个人至少3人认识,结论成立。若bc,bd,cd三者都为蓝色,则这6个人存在3个人互不认识,结论成立。😏

该定理等价于证明这6个顶点的完全图的边,用红、蓝二色任意着色,必然至少存在一个红色边三角形,或蓝色边三角形

下面有个比赛题目hdu5917题意:
无向图,求这个图有多少个点的集合,每个集合至少包含一个关于这个图的独立集或者团。并且集合的元素个数要大于等于3。(其实看到这种题还停留到只能想到并查集😅)

思路:运用Ramsey定理,如果该题城市数超过6个(包含6)一定存在这样的独立集,那么就直接求组合数就好了;如果城市数少于6个,当然还要大于等于3,也就是如果城市数是3,4,5就直接一一枚举。当城市数大于6时:(C(n,0)+C(n,1)…C(n,n)=pow(2,n),减去0到5。

#include <iostream>
#include <cstring>
#include <stdio.h>
#define ll long long
using namespace std;

bool v[55][55];
ll c[55][55];
const int mod=1e9+7;
ll ans;
int n,m;
void init()//用到杨辉三角,二项式
{
    c[0][1]=c[1][1]=1;
    for(int i=2;i<=m;i++)
        for(int j=0;j<=i;j++){
        if(j==0) c[j][i]=1;
        else c[j][i]=(c[j][i-1]+c[j-1][i-1])%mod;
    }
}
ll pow(ll a,ll b)//快速幂(前一个博客)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=res *a%mod;
        a=a*a%mod;
        b=b>>1;
    }
    return res;
}

bool judge(int a,int b,int c)
{
    if(v[a][b]&&v[a][c]&&v[b][c]) return true;
    if(!v[a][b]&&!v[a][c]&&!v[b][c]) return true;
    return false;
}
bool judge(int a,int b,int c,int d)
{
    if(judge(a,b,c)) return true;
    if(judge(b,c,d)) return true;
    if(judge(a,b,d)) return true;
    if(judge(a,c,d)) return true;
    return false;
}
bool judge(int a,int b,int c,int d,int e)
{
    if(judge(a,b,c,d)) return true;
    if(judge(a,b,c,e)) return true;
    if(judge(a,c,d,e)) return true;
    if(judge(b,c,d,e)) return true;
    if(judge(a,b,d,e)) return true;
    return false;
}

int main()
{
    int t,tt=0;
    cin>>t;
    while(t--)
    {
        tt++;
        cin>>m>>n;
        memset(v,false,sizeof(v));
        while(n--)
        {
            int i,j;
            cin>>i>>j;
            v[i][j]=true;
            v[j][i]=true;
        }
        ans=0;
        if(m>=6)
        {
            init();
            ans+=pow(2,m);
            for(int i=0;i<=5;i++)
            {
                ans-=c[i][m];
                ans+=mod;//
                ans%=mod;
            }
        }
        for(int i=1; i<=m; i++)
            for(int j=i+1; j<=m; j++)
                for(int k=j+1; k<=m; k++)
                    if(judge(i,j,k)) ans++;
        for(int i=1; i<=m; i++)
            for(int j=i+1; j<=m; j++)
                for(int k=j+1; k<=m; k++)
                    for(int l=k+1; l<=m; l++)
                        if(judge(i,j,k,l)) ans++;
        for(int i=1; i<=m; i++)
            for(int j=i+1; j<=m; j++)
                for(int k=j+1; k<=m; k++)
                    for(int l=k+1; l<=m; l++)
                        for(int u=l+1; u<=m; u++)
                            if(judge(i,j,k,l,u)) ans++;
          ans%=mod;
        printf("Case #%d: %lld\n",tt,ans);
    }

    return 0;
}

🍔🍟🌭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值