6445: 棋盘V
时间限制: 1 Sec 内存限制: 128 MB
提交: 325 解决: 31
[提交] [状态] [讨论版] [命题人:admin]
题目描述
有一块棋盘,棋盘的边长为100000,行和列的编号为1到100000。棋盘上有n个特殊格子,任意两个格子的位置都不相同。
现在小K要猜哪些格子是特殊格子。她知道所有格子的横坐标和纵坐标,但并不知道对应关系。换言之,她只有两个数组,一个存下了所有格子的横坐标,另一个存下了所有格子的纵坐标,而且两个数组都打乱了顺序。当然,小K猜的n个格子的位置也必须都不相同。
请求出一个最大的k,使得无论小K怎么猜,都能猜对至少k个格子的位置。
输入
输入数据第一行包含一个整数n。
接下来n行,每行描述一个特殊格子的位置。第i行含有两个整数xi和yi ,代表第i个格子的坐标。保证任意两个格子的坐标都不相同。
输出
输出一行,包含一个整数,代表最大的k。
样例输入
2
1 1
2 2
样例输出
0
提示
小K有可能会猜(1,2),(2,1),此时一个都没对
对于30%的数据,n≤8。
另外有5%的数据,所有横坐标和纵坐标均不相同。
另外有15%的数据,所有横坐标或者纵坐标均不相同。
对于100%的数据,n≤50,1≤xi,yi≤100000。
来源/分类
【题意】
对于题意,应该理解为,小K尽量猜错。
对于给出的坐标x和y,完成一个一一匹配,使得匹配完成后,与原坐标重复的最少。
【吐槽】
真的好想吐槽一下这道题。。。。其实应该是吐槽我自己。。。 这道题不算难 也挺好理解 跑一个费用流就OK了 可是我整整Debug了两天。。。没错 就是Debug了两天 感觉写的都对 试了好多个例子也都能过去 就是各种WA各种超时 还是因为我太菜 写代码经常忘掉一些细节。。。 比如终点的设置太小了 我却没发现 然后就一直死在这 。。
下面来谈谈这道题 题目中问的是至少猜对 其实就是让小K尽量去猜错
首先我们对输入的坐标离散化一下,这样方便建图。在正确的x,y之间建边容量=1,费用=1,不正确的x,y之间建边,容量=1,费用=0。然后设置一个虚拟的源点0,虚拟的终点(一个比较大的数,我就是因为设置小了 一直WA 自己一开始还没发现)
对0到所有的x坐标建边,容量为x行所含做标数,费用=0
对所有y到maxver建边,容量为y列所含做标数,费用=0;
对此图跑一遍费用流,按照费用流策略,优先走费用为0的边,即错误边,错误边满流时,不得不走正确边,花费1.
故最后的费用流即为不得不猜对的坐标。
【代码】
#include<bits/stdc++.h>
typedef long long ll;
//#define MAXN 200+10
//#define MAXM 1e5+3;
#define INF 0x3f3f3f3f
const int maxn=1e5+3;
const int MAX=4e2+3;
using namespace std;
int a[107],b[107],num[107];
int vist[107][107];
int ending=105;
struct node
{
int x;
int counts;
}numh[107],numl[107];
int cmp(node n,node m)
{
return n.counts>m.counts;
}
struct nodee{
int s,t,len,cap,flow,next;
}e[MAX*MAX];//len路长,cap最大流量,flow已流的量
int head[MAX],cnt;
void add(int u,int v,int len,int cap,int flow=0)
{
e[cnt]=nodee{u,v,len,cap,flow,head[u]};
head[u]=cnt++;
}
int dist[103];
bool vis[103];
int N, M,n;//点数 边数
int source, sink;//超级源点 超级汇点
int dis[maxn];//最短路
bool inq[MAX];
int f[MAX];//流量
int pre[maxn];//前驱边
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
bool spfa(int s,int t,int &flow,int &cost,int ver) //ver是最大点编号
{
for(int i=0;i<=ver;i++)dis[i]=INF;
memset(inq,0,sizeof(inq));
dis[s]=0;f[s]=INF;
queue<int>q;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
inq[u]=0;
for(int i=head[u];~i;i=e[i].next)
{
if(e[i].cap-e[i].flow>0&&dis[e[i].t]>dis[u]+e[i].len) //只要有流量,并且路径变短
{
f[e[i].t]=min(f[u],e[i].cap-e[i].flow);
dis[e[i].t]=dis[u]+e[i].len;
pre[e[i].t]=i;
if(!inq[e[i].t])
q.push(e[i].t),inq[e[i].t]=1;
}
}
}
if(dis[t]==INF) return 0; //没走到终点
flow+=f[t]; //本条增广路的流量
cost+=dis[t]*f[t]; //花费
for(int u=t;u!=s;u=e[pre[u]].s)
{
e[pre[u]].flow+=f[t];
e[pre[u]^1].flow-=f[t];
}
return 1;
}
int mincost(int s,int t,int ver)
{
int flow=0,cost=0;
while(spfa(s,t,flow,cost,ver));
return cost;
}
void getMap()
{
scanf("%d",&M);
for(int i=0;i<M;i++)
{
scanf("%d%d",&num[i],&num[M+i]);
b[i]=num[i],b[M+i]=num[M+i];
}
sort(num,num+2*M);
n = distance(num, unique(num, num + 2*M));//去重之后的总个数
for(int i = 0; i < 2*M; ++i)
{
a[i] = lower_bound(num, num + n, b[i]) - num + 1; // lower_bound返回迭代器,并计算位置
}
for(int i=0;i<M;i++)
vist[a[i]][a[i+M]]=1;
for(int i=0;i<M;i++)
{
add(a[i],a[i+M]+n+1,1,1);
add(a[i+M]+n+1,a[i],-1,0);
for(int j=M;j<2*M;j++)
if(vist[a[i]][a[j]]==0)
{
vist[a[i]][a[j]]=1;
add(a[i],a[j]+n+1,0,1);
add(a[j]+n+1,a[i],0,0);
}
}
for(int i=0;i<M;i++)
{
numh[a[i]].x=a[i];
numh[a[i]].counts++;
numl[a[i+M]].x=a[i+M];
numl[a[i+M]].counts++;
}
sort(numh,numh+n+1,cmp);
sort(numl,numl+n+1,cmp);
int i=0;
while(numh[i].counts!=0)
{
add(0,numh[i].x,0,numh[i].counts);
add(numh[i].x,0,0,0);
i++;
}
i=0;
while(numl[i].counts!=0)
{
add(numl[i].x+n+1,205,0,numl[i].counts);
add(205,numl[i].x+n+1,0,0);
i++;
}
}
int main()
{
init();
getMap();//建图
int ans=mincost(0,205,205);
printf("%d\n", ans);//最小费用 最大流
return 0;
}