【BZOJ4205】卡牌配对

卡牌配对

 

【问题描述】

现在有一种卡牌游戏,每张卡牌上有三个属性值:A,B,C。把卡牌分为X,Y两类,分别有n1,n2张。

两张卡牌能够配对,当且仅当,存在至多一项属性值使得两张卡牌该项属性值互质,且两张卡牌类别不同。

比如一张X类卡牌属性值分别是225,233,101,一张Y类卡牌属性值分别为115,466,99。那么这两张牌是可以配对的,因为只有10199一组属性互质。

游戏的目的是最大化匹配上的卡牌组数,当然每张卡牌只能用一次。

 

【输入】

数据第一行两个数n1n2,空格分割。

接下来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配对。

另外,请大胆使用渐进复杂度较高的算法!

 

【数据规模与约定】

对于10%的数据,n1,n2≤ 10

对于50%的数据,n1,n2≤ 3000

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



首先,可以发现这是一个二分图匹配,只不过点有点多(多到你的匈牙利就算能跑(n^2)也会T)

最近刷了一堆题,题解都写的是:数据太水,xxx即可跑过 或是 显然,不会有这么极端的数据

所以我打了一个用vector存图的匈牙利算法就走了。

然后。。。。。。

我们要找方法优化,注意到连边时有一些性质,只要有两个位置上的数他们一一不互质即有边,

那么

我们采用将边分类的方法优化

将质因数有x的分一类

那么对于一些数,他们都满足:第一个数的质因数有x,第二个数的质因数有y

他们之间就要两两连边,十分浪费,建立一个中枢节点,每一条边就可以表示为

从一端点到中枢到另一端点两条边,就将(n^2)级别的边数转化为了O(n)


这种思想就类似于数据结构中的线段树,

新增一个节点来更好的管理下方数量庞大的节点,

将数据分类编织,

最终。。。。。。

被这个2000000条边的isap卡常数

我将isap与Dinic结合才通过了全部数据

AC code:


//华而不实的代码,T而不A的人生
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<ctime>
#define inf 0x3f3f3f3f
#define maxm 2000005
#define maxn 70005
using namespace std;

int n,m;
int d[3][50][50];//缺第i个位置,另外两个位置按顺序为j,k的点所联通
int Prev[maxm],to[maxm],info[maxn],cnt_e=1,cap[maxm];

int t1;

inline void get(int &res){
	char ch;
	while(!isdigit(ch=getchar()));
	for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}

inline void Node(int u,int v,int c){
	cnt_e++;
	Prev[cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c;
}

inline void Line(int u,int v,int c){
	Node(v,u,0);
	Node(u,v,c);
}

int tot,s,t;

int New(){
	return ++tot;
}

int pr[50],cnt;
void prime(int up){
	bool vis[205]={};
	for(int i=2;i<=up;i++){
		if(!vis[i])
			pr[cnt++]=i;
		for(int j=0;j<cnt && pr[j]*i<=up;j++)
		{
			vis[pr[j]*i]=1;
			if(i%pr[j]==0) break;
		}
	}
	
	s=New(),t=New();
	for(int i=0;i<cnt;i++)
		for(int j=0;j<cnt;j++)
			for(int k=0;k<3;k++)
				d[k][i][j]=New();
}

int c[3][5],tag[3];

inline void Divide(int *r,int &cntnum,int x){//r存放质因子,分解x
	cntnum=0;
	for(int i=0;i<cnt && pr[i]<=x;i++)
		if(x%pr[i]==0)
			r[cntnum++]=i;
}

bool flag;
int stm,dis[maxn],vd[maxn];

int Q[maxn],L,R;
inline bool BFS(){
    L=R=0;
    Q[R++]=t;
    memset(dis,-1,sizeof dis);
    dis[t]=0;vd[0]++;
    int now;
    while(L<R){
        now=Q[L++];
        for(int i=info[now];i;i=Prev[i])
            if(cap[i^1] && dis[to[i]]==-1)
            {
                dis[to[i]]=dis[now]+1;
				vd[dis[to[i]]]++;
                Q[R++]=to[i];
            }
    }
    return dis[s]!=-1;
}

int aug(int now,int Max){
    if(now==t) 
        return Max;
    int st=Max,inc,Min=tot-1;
    for(int i=info[now];i;i=Prev[i])
        if(cap[i]){
            if(dis[to[i]]+1==dis[now]){
                inc=aug(to[i],min(cap[i],st));
                cap[i]-=inc;
                cap[i^1]+=inc;
                st-=inc;
                if(!st || flag) return Max-st;
            }
            Min=min(Min,dis[to[i]]);
        }
         
    if(Max==st){
        !(--vd[dis[now]]) && (flag=1);
        vd[dis[now]=Min+1]++;
    }
     
    return Max-st;
}
 
int sap(){
    memset(dis,0,sizeof d);
    memset(vd,0,sizeof vd);
    vd[0]=tot;
    stm=flag=0;
	BFS();
	int t2=0;
    while(!flag){
		stm+=aug(s,inf);
		//printf("%d\n",clock()-t1);
		t2++;
		if(t2%6==0) if(!BFS()) break;
	}
    return stm;
}

int main(){
	
	
	//freopen("1.in","r",stdin);
	
	int x,y,z;
	
	prime(200);
	get(n),get(m);
	for(int i=1;i<=n;i++){
		int u=New();
		Line(s,u,1);
		get(x),get(y),get(z);
		
		Divide(c[0],tag[0],x);
		Divide(c[1],tag[1],y);
		Divide(c[2],tag[2],z);
		
		for(int i=0;i<tag[0];i++)
			for(int j=0;j<tag[1];j++)
				Line(u,d[2][c[0][i]][c[1][j]],inf);
		
		for(int i=0;i<tag[1];i++)
			for(int j=0;j<tag[2];j++)
				Line(u,d[0][c[1][i]][c[2][j]],inf);
				
		for(int i=0;i<tag[0];i++)
			for(int j=0;j<tag[2];j++)
				Line(u,d[1][c[0][i]][c[2][j]],inf);
	}
	
	for(int i=1;i<=m;i++){
		int u=New();
		Line(u,t,1);
		get(x),get(y),get(z);
		
		Divide(c[0],tag[0],x);
		Divide(c[1],tag[1],y);
		Divide(c[2],tag[2],z);
		
		for(int i=0;i<tag[0];i++)
			for(int j=0;j<tag[1];j++)
				Line(d[2][c[0][i]][c[1][j]],u,inf);
		
		for(int i=0;i<tag[1];i++)
			for(int j=0;j<tag[2];j++)
				Line(d[0][c[1][i]][c[2][j]],u,inf);
				
		for(int i=0;i<tag[0];i++)
			for(int j=0;j<tag[2];j++)
				Line(d[1][c[0][i]][c[2][j]],u,inf);
	}
	//printf("%d\n",clock()-t1);
	
	printf("%d\n",sap());
	//printf("%d\n",clock()-t1);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值