BZOJ-4205 卡牌配對 最大流+線性篩+神建模

上午蛋蛋叫我去幫他調最大流,去了...
蛋蛋說,我莫名的沒法運行了...
手動枚舉錯誤,無果,掃描程序,無果...
    怒了,我交上去看看情況,再幫你調....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;
}
發佈了164 篇原創文章 · 獲贊 9 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章