• BZOJ4205卡牌配对——最大流+建图优化


    题目描述

    现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。
    两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。
    比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有101和99一组属性互质。
    游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。

    输入

    数据第一行两个数n1,n2,空格分割。
    接下来n1行,每行3个数,依次表示每张X类卡牌的3项属性值。
    接下来n2行,每行3个数,依次表示每张Y类卡牌的3项属性值。

    输出

    输出一个整数:最多能够匹配的数目。

    样例输入

    2 2
    2 2 2
    2 5 5
    2 2 5
    5 5 5

    样例输出

    2
    【提示】
    样例中第一张X类卡牌和第一张Y类卡牌能配对,第二张X类卡牌和两张Y类卡牌都能配对。所以最佳方案是第一张X和第一张Y配对,第二张X和第二张Y配对。
    另外,请大胆使用渐进复杂度较高的算法!

    提示

    对于100%的数据,n1,n2≤ 30000,属性值为不超过200的正整数
     
     

     乍一看就是一个简单的二分图最大匹配,枚举任意两个不同类的卡牌连边即可,但光是枚举的时间复杂度就爆炸了,何况还会跑$10^9$条边的最大流。所以我们想办法优化:先考虑两张牌$A,B$属性都分别不互质的情况(剩下两种情况相同),我们找到$A$属性是$x$的倍数、$B$属性是$y$的倍数的所有点,在二分图中间新建一个点然后将左右满足要求的点都连向这个点,流量为$INF$,表示这些点之间都能互相匹配。这里的$x,y$显然只需要枚举质数即可,$200$之内的质数只有$46$个,所以对于一种情况只需要建$46*46$个点即可,总点数为$70000$。而$2*3*5*7>200$,所以每个数最多只有三个质因子,每个点最多连$3*3*3$条边,总边数为$2000000$,直接跑$dinic$即可。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define INF 0x3f3f3f3f
    #define ll long long
    using namespace std;
    int head[70000];
    int to[4000000];
    int next[4000000];
    int val[4000000];
    int d[70000];
    int q[70000];
    int back[70000];
    int S,T;
    int n,m;
    int tot=1;
    int ans;
    int cnt;
    vector<int>v[210];
    int s[210][210];
    int a1[40000];
    int b1[40000];
    int c1[40000];
    int a2[40000];
    int b2[40000];
    int c2[40000];
    int prime[50];
    int vis[210];
    int num;
    void find()
    {
    	for(int i=2;i<=200;i++)
    	{
    		if(!vis[i])
    		{
    			prime[++cnt]=i;
    		}
    		for(int j=1;j<=cnt&&prime[j]*i<=200;j++)
    		{
    			vis[i*prime[j]]=1;
    			if(i%prime[j]==0)
    			{
    				break;
    			}
    		}
    	}
    	for(int i=1;i<=200;i++)
    	{
    		for(int j=1;j<=cnt;j++)
    		{
    			if(i%prime[j]==0)
    			{
    				v[i].push_back(j);
    			}
    		}
    	}
    	for(int i=1;i<=cnt;i++)
    	{
    		for(int j=1;j<=cnt;j++)
    		{
    			s[i][j]=++num;
    		}
    	}
    }
    void add(int x,int y,int v)
    {
        tot++;
        next[tot]=back[x];
        back[x]=tot;
        to[tot]=y;
        val[tot]=v;
        tot++;
        next[tot]=back[y];
        back[y]=tot;
        to[tot]=x;
        val[tot]=0;
    } 
    bool bfs(int S,int T)
    {
        int r=0;
        int l=0;
        memset(d,-1,sizeof(d));
        q[r++]=T;
        d[T]=2;
        while(l<r)
        {
            int now=q[l];
            for(int i=back[now];i;i=next[i])
            {
                if(d[to[i]]==-1&&val[i^1]!=0)
                {
                    d[to[i]]=d[now]+1;
                    q[r++]=to[i];
                }
            }
            l++;
        }
        if(d[S]==-1)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    int dfs(int x,int flow)
    {
        if(x==T)
        {
            return flow;
        }
        int now_flow;
        int used=0;
        for(int &i=head[x];i;i=next[i])
        {
            if(d[to[i]]==d[x]-1&&val[i]!=0)
            {
                now_flow=dfs(to[i],min(flow-used,val[i]));
                val[i]-=now_flow;
                val[i^1]+=now_flow;
                used+=now_flow;
                if(now_flow==flow)
                {
                    return flow;
                }
            }
        }
        if(used==0)
        {
            d[x]=-1;
        }
        return used;
    }
    int dinic()
    {
    	int res=0;
        while(bfs(S,T))
        {
            memcpy(head,back,sizeof(back));
            res+=dfs(S,0x3f3f3f3f);
        }
        return res;
    }
    int main()
    {
    	find();
    	scanf("%d%d",&n,&m);
    	S=n+m+46*46*3+1;
    	T=S+1;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d%d",&a1[i],&b1[i],&c1[i]);
    	}
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&a2[i],&b2[i],&c2[i]);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		add(S,i,1);
    		int sz1=v[a1[i]].size();
    		int sz2=v[b1[i]].size();
    		for(int j=0;j<sz1;j++)
    		{
    			for(int k=0;k<sz2;k++)
    			{
    				add(i,n+m+s[v[a1[i]][j]][v[b1[i]][k]],INF);
    			}
    		}
    		sz2=v[c1[i]].size();
    		for(int j=0;j<sz1;j++)
    		{
    			for(int k=0;k<sz2;k++)
    			{
    				add(i,n+m+46*46+s[v[a1[i]][j]][v[c1[i]][k]],INF);
    			}
    		}
    		sz1=v[b1[i]].size();
    		for(int j=0;j<sz1;j++)
    		{
    			for(int k=0;k<sz2;k++)
    			{
    				add(i,n+m+2*46*46+s[v[b1[i]][j]][v[c1[i]][k]],INF);
    			}
    		}
    	}
    	for(int i=1;i<=m;i++)
    	{
    		add(n+i,T,1);
    		int sz1=v[a2[i]].size();
    		int sz2=v[b2[i]].size();
    		for(int j=0;j<sz1;j++)
    		{
    			for(int k=0;k<sz2;k++)
    			{
    				add(n+m+s[v[a2[i]][j]][v[b2[i]][k]],n+i,INF);
    			}
    		}
    		sz2=v[c2[i]].size();
    		for(int j=0;j<sz1;j++)
    		{
    			for(int k=0;k<sz2;k++)
    			{
    				add(n+m+46*46+s[v[a2[i]][j]][v[c2[i]][k]],n+i,INF);
    			}
    		}
    		sz1=v[b2[i]].size();
    		for(int j=0;j<sz1;j++)
    		{
    			for(int k=0;k<sz2;k++)
    			{
    				add(n+m+2*46*46+s[v[b2[i]][j]][v[c2[i]][k]],n+i,INF);
    			}
    		}
    	}
    	printf("%d",dinic());
    }
  • 相关阅读:
    find 用法
    linux 查看链接库的版本
    虚函数重载(overwrite) 继承覆盖问题
    将iso mount 到nfs 目录问题
    centos 下使用 pytesseract 识别文字
    nginx 报错Malformed HTTP request line, git 报错fatal: git-write-tree: error building trees
    nfs 支持ipv6
    数位操作
    二分图(最小顶点覆盖 最大匹配 最大独立集 )
    欧几里得算法
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10584701.html
Copyright © 2020-2023  润新知