卡牌配对-网络流

卡牌配对(bzoj4205)-网络流

题目描述

题目描述
题目描述

题解

一,暴力建边+匈牙利算法

时间复杂度 O ( n 2 ) O(n^2) O(n2),可以得六十分

代码实现
#include<bits/stdc++.h> 
#define M 100009
using namespace std;
vector<int>e[M];
struct card{
	int x,y,z;
}p[M],q[M];
int n,m,match[M],vis[M],ans,cnt;
int gcd(int x,int y){
	if(!y) return x;
	return gcd(y,x%y);
}
bool solve(int x){
	for(int i=0;i<e[x].size();i++)
		if(!vis[e[x][i]]){
			vis[e[x][i]]=1;
			if(!match[e[x][i]]||solve(match[e[x][i]])){
				match[e[x][i]]=x;
				return 1;
			}
		}return 0;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			cnt=0;
			if(gcd(p[i].x,q[j].x)==1) cnt++;
			if(gcd(p[i].y,q[j].y)==1) cnt++;
			if(gcd(p[i].z,q[j].z)==1) cnt++;
			if(cnt<=1) e[i].push_back(j);
		}
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		ans+=solve(i);
	}printf("%d\n",ans);
	return 0;
} 
二,分类+网络流

观察数据范围,属性值都小于200.而200以内的质数只有40多个,所以考虑分为3类,分别是AB,AC,BC。以AB类举例子,如果一张X类卡牌的A能被质数a整除,B能被质数b整除,则由该卡牌向(a,b)连一条容量为1的边。反之如果是Y类卡牌,则由(a,b)向该卡牌连边。如果连接在同一个质数点对的X类卡牌和Y类卡牌必能匹配,最后连上源点,汇点,跑最大流即可

代码实现
#include<bits/stdc++.h>//网络流建模 
#define M 1100009
using namespace std;
vector<int>pre[220];
int tot=1,nxt[M],first[M],to[M],w[M],d[M],now[M];
int n,m,sum,cnt,num[221][211],T,prime[221];
const int inf=1e9;
void add(int x,int y,int z){
	nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
	nxt[++tot]=first[y],first[y]=tot,to[tot]=x,w[tot]=0;
}
bool bfs(){
	memset(d,0,sizeof(d));
	d[0]=1,now[0]=first[0];
	queue<int>q;
	q.push(0);
	while(q.size()){
		int u=q.front();
		q.pop();
		for(int i=first[u];i;i=nxt[i]){
			int v=to[i];
			if(w[i]&&!d[v]){
				d[v]=d[u]+1,now[v]=first[v];
				q.push(v);
				if(v==T) return 1;
			}
		}
	}return 0;
}
int dfs(int x,int flow){
	if(x==T) return flow;
	int rest=flow,i;
	for(i=now[x];i&&rest;i=nxt[i]){
		int v=to[i];
		if(w[i]&&d[v]==d[x]+1){
			int k=dfs(v,min(rest,w[i]));
			w[i]-=k,w[i^1]+=k,rest-=k;
		}
	}now[x]=i;
	return flow-rest;
}
int dinic(){
	int ans=0;
	while(bfs()) ans+=dfs(0,inf);
	return ans;
}
void init(){
	for(int i=2;i<=200;i++) prime[i]=1;
	for(int i=2;i<=200;i++)
		for(int j=2;j<=i;j++)
			if(prime[j]&&i%j==0){pre[i].push_back(j);if(i!=j) prime[i]=0;}
	for(int i=2;i<=200;i++)
		for(int j=2;j<=200;j++)
			if(prime[i]&&prime[j]) num[i][j]=++cnt;
}
int main(){
	int x,y,z;
	scanf("%d%d",&n,&m);
	init(),sum=cnt*3,T=sum+n+m+1;
	for(int i=1;i<=n;i++){
		sum++;
		scanf("%d%d%d",&x,&y,&z);add(0,sum,1);
		for(int j=0;j<pre[x].size();j++)
			for(int k=0;k<pre[y].size();k++) 
				add(sum,num[pre[x][j]][pre[y][k]],1);
		for(int j=0;j<pre[x].size();j++)
			for(int k=0;k<pre[z].size();k++) 
				add(sum,num[pre[x][j]][pre[z][k]]+cnt,1);
		for(int j=0;j<pre[y].size();j++)
			for(int k=0;k<pre[z].size();k++) 
				add(sum,num[pre[y][j]][pre[z][k]]+cnt*2,1);
		
	}
	for(int i=1;i<=m;i++){
		sum++;
		scanf("%d%d%d",&x,&y,&z);
		for(int j=0;j<pre[x].size();j++)
			for(int k=0;k<pre[y].size();k++) 
				add(num[pre[x][j]][pre[y][k]],sum,1);
		for(int j=0;j<pre[x].size();j++)
			for(int k=0;k<pre[z].size();k++) 
				add(num[pre[x][j]][pre[z][k]]+cnt,sum,1);
		for(int j=0;j<pre[y].size();j++)
			for(int k=0;k<pre[z].size();k++) 
				add(num[pre[y][j]][pre[z][k]]+cnt*2,sum,1);
		add(sum,T,1);
	}//printf("%d %d %d",cnt,sum,T);
	//printf("%d\n",tot);
	printf("%d\n",dinic());
	return 0;
}

做题启发

1,善于观察数据范围,特定的数据范围往往是题目的切入点
2,善于转化问题,逆向思维非常重要

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值