【BZOJ2744】【codevs2366】朋友圈,二分图最大匹配

传送门1
传送门2
思路:
思维不错的一道题。
题意就是给出限制条件,在构建出的图上找最大团,如果是一般图找最大团是NPC问题,所以要转换思路,一看数据范围很像网络流,但是一开始我并没有想到怎么做,也并没有什么比较好的建图方式。
最初的想法就是A国只能选两个(显然),关键在于B国怎么选,B国朋友的限制条件是”或”,所以并不能直接建二分图,绞尽脑汁想了很久也没有什么办法,偷瞄了一眼题解(啪),看到有一句话说要考虑逆问题,直接ctrl+w后继续想,发现如果把B国朋友的限制条件反过来,那么这正是二分图的建图条件,感觉自己还是too young。
把这个瓶颈突破以后,之后的工作就比较容易了,枚举A国0个,1个,2个,然后对B国中符合条件的人分奇偶,建二分图并连边,跑匈牙利或dinic就可以了。
吐槽一下:这个题的数据太水了,我一开始写了一个不清空vis数组的匈牙利得了90分,还发现WA的10分并不是匈牙利的事情,而是我没判断选A国两个人时它们的xor值是否是奇数!判断以后就A了,唉
回到正题,如果每次暴力记录vis的时间戳以及当前点可不可行的话,跑匈牙利是跑不过的(好像只能有70分),学习了一下优化技巧,每次存下来合法的点,然后把图重构一下,这样就可以过剩下的3个点了
设点数为n,边数为m,匈牙利的时间复杂度最坏是 O(nm) ,dinic的单位流容量复杂度是 O(mn) ?我的大部分题写的都是dinic,好像只有一两道没法用dinic,只能套匈牙利
提醒一下,并没有什么多组数据,BZOJ和codevs上甚至连数据组数都不用输入
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#define inf 1e9
using namespace std;
int T,A,B,M;
int a[205],b[3005],X[3005],Y[3005];
bool ok[205][3005],not_friend[3005][3005];
namespace network
{
    int s,t,tot,first[3005],dis[3005];
    queue<int>q;
    struct edge{
        int v,w,next;
    }e[6000005];
    void init()
    {
        memset(first,0,sizeof(first));
        tot=1;s=0;t=B+1;
    }
    void add(int x,int y,int z)
    {
        e[++tot].v=y;e[tot].w=z;e[tot].next=first[x];first[x]=tot;
        e[++tot].v=x;e[tot].w=0;e[tot].next=first[y];first[y]=tot;
    }
    bool bfs()
    {
        memset(dis,0,sizeof(dis));
        int x;
        dis[s]=1;
        for (q.push(s);!q.empty();q.pop())
        {
            x=q.front();
            for (int i=first[x];i;i=e[i].next)
                if (!dis[e[i].v]&&e[i].w)
                    dis[e[i].v]=dis[x]+1,
                    q.push(e[i].v);
        }
        return dis[t]>0;
    }
    int dfs(int x,int maxn)
    {
        if (x==t) return maxn;
        int used=0,k;
        for (int i=first[x];i;i=e[i].next)
            if (dis[e[i].v]==dis[x]+1)
            {
                k=dfs(e[i].v,min(maxn-used,e[i].w));
                e[i].w-=k;e[i^1].w+=k;
                used+=k;
                if (used==maxn) return maxn;
            }
        if (!used) dis[x]=0;
        return used;
    }
}
main()
{
    scanf("%d%d%d",&A,&B,&M);
    for (int i=1;i<=A;++i) scanf("%d",a+i);
    for (int i=1;i<=B;++i) scanf("%d",b+i);
    for (int u,v,i=1;i<=M;++i)
        scanf("%d%d",&u,&v),
        ok[u][v]=1;
    network::init();
    int tmp,num;
    for (int i=1;i<=B;++i)
    {
        for (int j=1;j<i;++j)
        if ((b[i]^b[j])&1)
        {
            tmp=(b[i]|b[j]);
            num=0;
            for (;tmp;tmp^=tmp&-tmp) ++num;
            if (num&1);
            else not_friend[i][j]=not_friend[j][i]=1;
        }
        if (b[i]&1) X[++X[0]]=i,network::add(network::s,i,1);
        else Y[++Y[0]]=i,network::add(i,network::t,1);
    }
    for (int i=1;i<=X[0];++i)
        for (int j=1;j<=Y[0];++j)
            if (not_friend[X[i]][Y[j]])
                network::add(X[i],Y[j],1);
    int ans=B;
    while (network::bfs()) ans-=network::dfs(network::s,inf);
    int sum;
    for (int i=1;i<=A;++i)
    {
        X[0]=Y[0]=0;
        network::init();
        for (int j=1;j<=B;++j)
        {
            if (ok[i][j])
                if (b[j]&1) X[++X[0]]=j,network::add(network::s,j,1);
                else Y[++Y[0]]=j,network::add(j,network::t,1);
        }
        sum=X[0]+Y[0]+1;
        for (int j=1;j<=X[0];++j)
            for (int k=1;k<=Y[0];++k)
                if (not_friend[X[j]][Y[k]]) network::add(X[j],Y[k],1);
        while (network::bfs()) sum-=network::dfs(network::s,inf);
        ans=max(sum,ans);
    }
    for (int i=1;i<=A;++i)
        for (int j=i+1;j<=A;++j)
        if ((a[i]^a[j])&1)
        {
            X[0]=Y[0]=0;
            network::init();
            for (int k=1;k<=B;++k)
            {
                if (ok[i][k]&&ok[j][k])
                    if (b[k]&1) X[++X[0]]=k,network::add(network::s,k,1);
                    else Y[++Y[0]]=k,network::add(k,network::t,1);
            }
            sum=X[0]+Y[0]+2;
            for (int k=1;k<=X[0];++k)
                for (int l=1;l<=Y[0];++l)
                    if (not_friend[X[k]][Y[l]]) network::add(X[k],Y[l],1);
            while (network::bfs()) sum-=network::dfs(network::s,inf);
            ans=max(sum,ans);
        }
    printf("%d\n",ans); 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值