题目描述
有一块棋盘,棋盘的边长为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,这和最小费用最大流很像,于是我们考虑下述构图。
这时,我们发现要再匹配边数为n时求最小的k,这和最小费用最大流很像,于是我们考虑下述构图。
对
于
任
一
x
、
y
,
从
s
向
x
连
容
量
为
x
出
现
次
数
,
费
用
为
0
的
边
。
从
y
向
t
连
容
量
为
y
出
现
次
数
,
费
用
为
0
的
边
。
对于任一x 、y,从s向x连容量为x出现次数,费用为0的边。从y向t连容量为y出现次数,费用为0的边。
对于任一x、y,从s向x连容量为x出现次数,费用为0的边。从y向t连容量为y出现次数,费用为0的边。
同
时
,
若
(
x
,
y
)
是
合
法
点
,
则
从
x
向
y
连
容
量
为
1
费
用
为
1
的
边
。
反
之
连
容
量
为
1
费
用
为
0
的
边
。
同时,若(x,y)是合法点,则从x向y连容量为1费用为1的边。反之连容量为1费用为0的边。
同时,若(x,y)是合法点,则从x向y连容量为1费用为1的边。反之连容量为1费用为0的边。
跑
最
小
费
用
最
大
流
,
这
时
最
大
流
满
足
一
共
选
了
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
*/