【2016-CCPC-K】匈牙利算法,最大基数匹配(Kingdom of Obsession,hdu 5943)

来源:互联网 发布:smb协议端口 编辑:程序博客网 时间:2024/05/20 07:53

打的重现塞,比赛时第一个就看这题,看完后一脸懵逼,觉得除了暴力贪心枚举以外想不到其他解法了。而且这样贪心还肯定是错的= =。

n个数给每个数都找一个位子,使得满足条件。事实上这就是一个匹配问题,自己做的题太少了,所以没感觉。

n和s最大可以取到1e9。这么大的数据怎么办呢?答案就是要么先找找规律取取巧,把问题化简,要么就换思路。

这题就是把区间[1+s,n+s]的数放到区间[1,n]中。

设x为区间[1+s,n+s]中的数,y为区间[1,n]中的数。

要让所有的x满足x%y==0。

先考虑两个区间相交的情况。

那么相交部分就直接放到对应编号的位子上就好了,因为x都是最小的那些,y都是最大的那些。


试着证明一下。

关键是怕抢了不该抢的位子是不。

如果x只能放在这里,那就不算抢。

否则设x还可以放在Y,Y<y,x%Y==0。

对于某个X>x,若可以放在y,则X%y==0,因为y==x,且x%Y==0,推出X%Y==0,所以X也一定能放在Y,所以也没有抢。


那剩下的怎么办呢,或者说终究得考虑区间不相交的情况。

因为素数只能放在1号或自己编号的位子,所以若区间[s+1,n+s]中若有2个以上的素数,那就至少有一个素数没地方放,那就绝对不行。我们知道两个素数之间的间隔是很小的(相对于素数的大小而言)

http://www.guokr.com/post/474460/

printf("%d\n",int(log(n)*(log(n)-2*log(log(n)))+2));
因此如果区间太大,那就直接输出No就好了。

只有区间足够小,我们才需要去另谋出路。

数据降下来了,一切就好办了,特别是如果能意识到这是一个匹配问题,然后用二分图最大基数匹配建模,用匈牙利算法计算就很简单了。

关键比较难想到吧,要绕好几个弯呢。

至于区间不相交的情况即s>=n时,我们发现只要交换一下s,n就可以直接套用上述解法,不过这都不重要了。


代码

#include<bits/stdc++.h>#define maxn 1010using namespace std;typedef long long ll;ll n,s;inline bool ss(ll n){    ll m=(ll)sqrt(n+0.5);    for(ll i=2;i<=m;i++)        if(n%i==0) return false;    return true;}bool OK(){    ll cnt=0;    for(ll i=n+1;i<=n+s;i++)    {        if(ss(i)) cnt++;        if(cnt>=2) return false;    }    return true;}/////////////////////////////////////hungarybool MAP[maxn][maxn];ll link[maxn];bool vis[maxn];bool dfs(ll u){    for(ll v=1;v<=s;v++)    {        if(MAP[u][v]&&!vis[v])        {            vis[v]=true;            if(!link[v]||dfs(link[v]))            {                link[v]=u;                return true;            }        }    }    return false;}ll hungary(){    ll ret=0;    memset(MAP,0,sizeof(MAP));    for(ll u=1;u<=s;u++)    {        for(ll v=1;v<=s;v++)        {            if((u+n)%v==0) MAP[u][v]=1;        }    }    memset(link,0,sizeof(link));    for(ll u=1;u<=s;u++)    {        memset(vis,0,sizeof(vis));        if(dfs(u)) ret++;    }    return ret;}///////////////////////////////////bool ok(){    if(!OK()) return false;    if(hungary()==s) return true;    else return false;}int main(){    ll T;    scanf("%I64d",&T);    for(ll t=1;t<=T;t++)    {        scanf("%I64d %I64d",&n,&s);        if(s>n) swap(s,n);        printf("Case #%I64d: ",t);        if(ok()) puts("Yes");        else puts("No");    }    return 0;}


0 0
原创粉丝点击