[网络流24题-2]cogs396魔术球问题

来源:互联网 发布:上海迅销网络招聘真假 编辑:程序博客网 时间:2024/05/24 06:48

至于那个贪心的证明我是没整出来的。。。看了黑书,可能是我没有认真看吧。

最小路径点覆盖:用最少的边去覆盖尽可能多的点(全部)。

黑书上介绍了一个求最小路径点覆盖的方法,很值得借鉴,那就是每个点i转化为一个ii,当原图存在边(i,j)时,连边(i,j),求这个ST割的最大二分图匹配,再用点数-这个值即为最小路径点覆盖。

为什么呢?因为假定原图每条边只对应一个点,那么n个点需要n条边去覆盖。每存在一条连了2个点的边就可以省去一条边,而这个二分图匹配能保证是互不有重叠点的边集的最大值,所以肯定最多能省去这么多条边;能省去这么多条边后自然省去每条边对应的那个点;因而用总点数-最大匹配即为答案。

具体到这个题,就是把两个和为完全平方数的两球连一条边,枚举,数据比较小,能过。而且我并不觉得设一个上界和下界去二分能提升多大的速度,所以直接用for枚举。

唯一注意的是,上一次枚举完的边还可以用的 要加的边只是所有点到多的点还要加的边。然后这样的话跑cogs的数据足够。。。。

然后还是要用匈牙利算法求最大匹配;judge函数用来判断是否和为平方数。

代码

#include <iostream>#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <vector>#include <queue>#include <cmath>//ACusing namespace std;const int maxn=10000;vector<int > g[maxn];void addedge(int from,int to){    g[from].push_back(to);    g[to].push_back(from);}int match[maxn];bool book[maxn];int n;bool judge(int n){    double nn=sqrt(n);    if((int)n/nn==nn)    {        return true;    }    return false;}bool dfs(int v){    for(unsigned i=0;i<g[v].size();i++)    {        int u=g[v][i];        if(!book[u])        {            book[u]=true;            if(match[u]==0 || dfs(match[u]))            {                match[u]=v;                match[v]=u;                return true;            }        }    }    return false;}bool cal(int res){    for(int i=1;i<=res-1;i++)    {        if(judge(i+res))        {            addedge(i,res+2000);        }    }    int ans=0;    for(int i=1;i<=res;i++)    {        if(match[i]==0)        {            memset(book,0,sizeof(book));            if(dfs(i))            {                ans++;            }        }    }    return (res-ans)<=n;}int main(){    //freopen("balla.in","r",stdin);    //freopen("balla.out","w",stdout);    scanf("%d",&n);    int ans=1;    while(ans++)    {        memset(book,0,sizeof(book));        memset(match,0,sizeof(match));        if(!cal(ans))        {            break;        }    }    printf("%d\n",ans-1);    return 0;}
0 0
原创粉丝点击