#2766. 猜测(guess)

题目描述

有一块棋盘,棋盘的边长为100000,行和列的编号为1到100000。棋盘上有n个特殊格子,任意两个格子的位置都不相同。

现在小K要猜哪些格子是特殊格子。她知道所有格子的横坐标和纵坐标,但并不知道对应关系。换言之,她只有两个数组,一个存下了所有格子的横坐标,另一个存下了所有格子的纵坐标,而且两个数组都打乱了顺序。当然,小K猜的n个格子的位置也必须都不相同。

请求出一个最大的k,使得无论小K怎么猜,都能猜对至少k个格子的位置。
输入格式

输入数据第一行包含一个整数n。

接下来n行,每行描述一个特殊格子的位置。

第i行含有两个整数x_i和y_i,代表第i个格子的坐标。保证任意两个格子的坐标都不相同。
输出格式

输出一行,包含一个整数,代表最大的k。
数据范围与提示

【样例解释1】

小K有可能会猜(1,2),(2,1),此时一个都没对。

【样例解释2】

此时只有一种合法的猜测。注意(1,1),(1,1),(2,2)不是一个合法的猜测。

【数据规模和约定】

对于30%的数据,n≤8。

另外有5%的数据,所有横坐标和纵坐标均不相同。

另外有15%的数据,所有横坐标或者纵坐标均不相同。

对于100%的数据,n≤50,1≤x_i,y_i≤100000。
来源

2016年6月湖南师大附中集训test2

题 解 : 题解:
根 据 题 意 , 考 虑 二 分 图 。 根据题意,考虑二分图。 ,
但 是 每 个 点 可 匹 配 的 个 数 不 是 1 。 所 以 转 为 求 网 络 流 。 但是每个点可匹配的个数不是1。所以转为求网络流。 1
这 时 , 我 们 发 现 要 再 匹 配 边 数 为 n 时 求 最 小 的 k , 这 和 最 小 费 用 最 大 流 很 像 , 于 是 我 们 考 虑 下 述 构 图 。 这时,我们发现要再匹配边数为n时求最小的k,这和最小费用最大流很像,于是我们考虑下述构图。 nk
对 于 任 一 x 、 y , 从 s 向 x 连 容 量 为 x 出 现 次 数 , 费 用 为 0 的 边 。 从 y 向 t 连 容 量 为 y 出 现 次 数 , 费 用 为 0 的 边 。 对于任一x 、y,从s向x连容量为x出现次数,费用为0的边。从y向t连容量为y出现次数,费用为0的边。 xysxx0yty0
同 时 , 若 ( x , y ) 是 合 法 点 , 则 从 x 向 y 连 容 量 为 1 费 用 为 1 的 边 。 反 之 连 容 量 为 1 费 用 为 0 的 边 。 同时,若(x,y)是合法点,则从x向y连容量为1费用为1的边。反之连容量为1费用为0的边。 (x,y)xy1110
跑 最 小 费 用 最 大 流 , 这 时 最 大 流 满 足 一 共 选 了 n 个 点 , 且 尽 量 用 最 坏 决 策 , 选 不 合 法 点 。 所 以 最 小 费 用 即 为 答 案 。 跑最小费用最大流,这时最大流满足一共选了n个点,且尽量用最坏决策,选不合法点。所以最小费用即为答案。 n

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#define N 105
using namespace std;
const int inf=1e9;
int n,a[N*2],ans;
int tot=1,head[N<<1],ver[N*N*2],nex[N*N*2],edge[N*N*2],cost[N*N*2];
int match[N*2],t,s;
struct node{
	int x,y;
}dian[N];
inline void add(int x,int y,int z,int val){
	nex[++tot]=head[x];head[x]=tot;ver[tot]=y;edge[tot]=z;cost[tot]=val;
}
int d[N<<1],v[N<<1],incf[N<<1],pre[N<<1];
bool spfa(){
	queue<int> q;
	for(int i=1;i<=t;++i)d[i]=inf;
	memset(v,0,sizeof(v));
	q.push(s);d[s]=0;v[s]=1;
	incf[s]=inf;
	while(!q.empty()){
		int x=q.front();q.pop();v[x]=0;
		for(int i=head[x];i;i=nex[i]){
			int y=ver[i];
			if(!edge[i])continue;
			if(d[y]>d[x]+cost[i]){
				d[y]=d[x]+cost[i];
				incf[y]=min(incf[x],edge[i]);
				pre[y]=i;
				if(!v[y])v[y]=1,q.push(y);
			}
		}
	}
	if(d[t]==inf)return false;
	return true;
}
void update(){
	int x=t;
	while(x!=s){
		int i=pre[x];
		edge[i]-=incf[t];
		edge[i^1]+=incf[t];
		x=ver[i^1];
	}
	ans+=d[t]*incf[t];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d",&a[i],&a[i+n]);
		dian[i].x=a[i],dian[i].y=a[i+n];
	}
	int lcnt=n,rcnt=n;
	sort(a+1,a+1+lcnt);
	lcnt=unique(a+1,a+1+lcnt)-a-1;
	sort(a+n+1,a+n+1+rcnt);
	rcnt=unique(a+n+1,a+n+1+rcnt)-a-n-1;
/*	cout<<endl;
	cout<<lcnt<<" "<<rcnt<<endl;
	for(int i=1;i<=lcnt;++i)cout<<a[i]<<" ";cout<<endl;
	for(int i=n+1;i<=n+rcnt;++i)cout<<a[i]<<" ";cout<<endl;*/
	t=n*2+1,s=n*2+2;
	for(int i=1;i<=lcnt;++i){
		for(int j=n+1;j<=n+rcnt;++j){
		    int flag=0;
			for(int k=1;k<=n;++k){
			    if(a[i]==dian[k].x&&a[j]==dian[k].y)flag=1;
			}
			if(flag){
				add(i,j,1,1);add(j,i,0,-1);
			}else{
				add(i,j,1,0);add(j,i,0,0);
			}
		}
	}
	for(int i=1;i<=lcnt;++i){
		int cc=0;
		for(int j=1;j<=n;++j){
			cc+=a[i]==dian[j].x;
		}
		add(s,i,cc,0);add(i,s,0,0);
	}
	for(int i=n+1;i<=n+rcnt;++i){
		int cc=0;
		for(int j=1;j<=n;++j){
			cc+=a[i]==dian[j].y;
		}
		add(i,t,cc,0);add(t,i,0,0);
	}
	while(spfa()){update();}
	//cout<<ans<<endl;
	printf("%d\n",ans);
	return 0;
}

/*
7
1 1
1 2
1 3
2 4
3 1
3 2
3 3
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值