BZOJ-4205 卡牌配对 最大流+线性筛+神建模

34 篇文章 0 订阅
30 篇文章 0 订阅
上午蛋蛋叫我去帮他调最大流,去了...
蛋蛋说,我莫名的没法运行了...
手动枚举错误,无果,扫描程序,无果...
    怒了,我交上去看看情况,再帮你调....A了....问:蛋蛋你咋搞的?蛋蛋:我不会,所以照着黄学长的打的....
.......于是怒去拍蛋蛋,这道题拍的心累.....

4205: 卡牌配对
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 102 Solved: 46
[Submit][Status][Discuss]

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

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

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

Sample Input
2 2
2 2 2
2 5 5
2 2 5
5 5 5

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

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

Source

这个题是真的厉害,想了好久都建不出图

题解:
首先对于暴力构图,非常的好想好实现,大约能过一半,显然不行
于是要寻找可以修改的共同点,对于互质,很容易想到,只需要满足为质数一定互质,于是从质数这个角度入手,筛出200内的质数(46个)
在最暴力的模型中加入一种点,满足要求的左右点分别与它相连,边权为正无穷。
考虑到x和y只需是质数,这样的点共有至多3*46*46个(1~200质数共46个),而200<2*3*5*7,所以两侧每个点至多连出3*3*3条边。
这样建出的图看似很大,并不能跑出来,但是仍旧是分层图,对于分层图,Dinic有着十分玄学的效率O(跑得过),当然题目中也作出提示,加上当前弧优化直接硬跑!

跑得没有蛋蛋快,不开心…谁让蛋蛋是黄学长的程序翻版呢…..

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxn 30010+30010+3*46*46+1
#define maxm 4000010
#define nn 30010
int n1,n2,S,T;
struct data{int to,next,cap;}edge[maxm];
int head[maxn]={0},cnt=1;
struct dat{int a,b,c;}x[nn],y[nn];

void add(int u,int v,int w)
{
    cnt++;
    edge[cnt].next=head[u]; head[u]=cnt;
    edge[cnt].to=v; edge[cnt].cap=w;
}
void insert(int u,int v,int w)
{
    add(u,v,w); add(v,u,0);
}
//add edge
int q[maxn],h,t;
int dis[maxn],cur[maxn];
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    q[0]=S; dis[S]=1; h=0;t=1;
    while (h<t)  
        {
            int j=q[h++];
            for (int i=head[j]; i; i=edge[i].next)
                if (edge[i].cap && dis[edge[i].to]<0)
                    dis[edge[i].to]=dis[j]+1,q[t++]=edge[i].to;
        }
    return dis[T]!=-1;
}
int dfs(int loc,int low)
{
    if (loc==T) return low;
    int w,used=0;
    for (int i=head[loc]; i; i=edge[i].next)
        if (dis[edge[i].to]==dis[loc]+1)
            {
                w=dfs(edge[i].to,min(low-used,edge[i].cap));
                used+=w; edge[i].cap-=w; edge[i^1].cap+=w;
                if (edge[i].cap) cur[loc]=i;
                if (used==low) return low;
            }
    if (!used) dis[loc]=-1;
    return used;
}
#define inf 0x7fffffff
int dinic()
{
    int tmp=0;
    while (bfs())
        {
            for (int i=S; i<=T; i++) cur[i]=head[i];
            tmp+=dfs(S,inf);
        }
    return tmp;
}
//Maxflow
vector<int>v[210];
int prime[210];bool flag[210];
void get_prime(int n)
{
    flag[1]=1;
    for (int i=2; i<=n; i++)
        {
            if (!flag[i]) prime[++prime[0]]=i;
            for (int j=1; j<=prime[0] && i*prime[j]<=n; j++)
                {
                    flag[i*prime[j]]=1;
                    if (!(i%prime[j])) break;
                }
        }
    for (int i=2; i<=n; i++)
        for (int j=1; j<=prime[0]; j++)
            if (!(i%prime[j])) v[i].push_back(j);
}
//get_prime
int id[50][50],idd=0;
void build(int t,int f)
{
    int a,b,c;
    if (!f) a=x[t].a,b=x[t].b,c=x[t].c;
       else a=y[t].a,b=y[t].b,c=y[t].c;
    for(int i=0; i<v[a].size(); i++)
        for(int j=0; j<v[b].size(); j++)
            if(!f) insert(t,n1+n2+id[v[a][i]][v[b][j]],1);
              else insert(n1+n2+id[v[a][i]][v[b][j]],n1+t,1);
    for(int i=0;i<v[a].size(); i++)
        for(int j=0;j<v[c].size(); j++)
            if(!f) insert(t,n1+n2+id[v[a][i]][v[c][j]]+46*46,1);
              else insert(n1+n2+id[v[a][i]][v[c][j]]+46*46,n1+t,1);
    for(int i=0; i<v[b].size(); i++)
        for(int j=0; j<v[c].size(); j++)
            if(!f) insert(t,n1+n2+id[v[b][i]][v[c][j]]+46*46*2,1);
              else insert(n1+n2+id[v[b][i]][v[c][j]]+46*46*2,n1+t,1);
}
void make()
{
    for(int i=1;i<=46;i++)
        for(int j=1;j<=46;j++)
            id[i][j]=++idd;
    S=0; T=n1+n2+3*46*46+1;
    for (int i=1; i<=n1; i++)
        insert(0,i,1),build(i,0);
    for (int i=1; i<=n2; i++)
        insert(n1+i,T,1),build(i,1);
}
//build
int main()
{
    n1=read(),n2=read();
    for (int i=1; i<=n1; i++) 
        x[i].a=read(),x[i].b=read(),x[i].c=read();
    for (int i=1; i<=n2; i++) 
        y[i].a=read(),y[i].b=read(),y[i].c=read();
    get_prime(200);make();
    printf("%d\n",dinic());
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值