棋盘V
时间限制: 1 Sec 内存限制: 128 MB
提交: 327 解决: 33
[提交] [状态] [讨论版] [命题人: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。
来源/分类
题意:
给你n对坐标,但是这里的x和y不一定原来匹配的,让你求他最少能猜对几个(在最坏的情况下能才对几个:无论怎么猜都会对几个),并且猜的对中没有一样的
题解;
刚开始想着贪心,枚举情况,后来发现情况太多 ,赛后补题,题解里用的网络流还有二分图最大匹配,我这里用的是最小费用最大流,如何见图是关键,我们将超级源点和每个互不相同的x进行连线,容量是x出现的次数,费用是零,让每个不相同的y和超级汇点连接,容量为y出现的次数,费用为零,假设给出的x,y对是唯一匹配的,(事实上不一定这个x就是配这个y的),将匹配的连线,费用为一,容量为1,x和不匹配的y也连线,只不过费用为0,容量依然为1,见图完毕
这里要注意的是,超级源点和超级汇点不能为负数,因为数组下标是0——n-1的。注意这两个点的值不能和中间的点的值重复,中间的点的标号也不能相同。
代码:
这里的最小费用最大流的板子来自其他博客,但是忘记是哪个了,如果板子的主人看到了,欢迎来认领
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include<map>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define MAXN 100003
#define MAXM 80000+100
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN];//记录增广路径上 到达点i的边的编号
int dist[MAXN];
bool vis[MAXN];
int N, M;//点数 边数
int source, sink;//超级源点 超级汇点
void init()
{
edgenum = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
Edge E1 = {u, v, w, 0, c, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, 0, 0, -c, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
bool SPFA(int s, int t)//寻找花销最少的路径
{
//跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流
//若存在 说明可以继续增广,反之不能
queue<int> Q;
memset(dist, INF, sizeof(dist));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dist[s] = 0;
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//可以松弛 且 没有满流
{
dist[E.to] = dist[u] + E.cost;
pre[E.to] = i;//记录前驱边 的编号
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
return pre[t] != -1;//可达返回true
}
void MCMF(int s, int t, int &cost, int &flow)
{
flow = 0;//总流量
cost = 0;//总费用
while(SPFA(s, t))//每次寻找花销最小的路径
{
int Min = INF;
//通过反向弧 在源点到汇点的最少花费路径 找最小增广流
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
Edge E = edge[i];
Min = min(Min, E.cap - E.flow);
}
//增广
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost * Min;//增广流的花销
}
flow += Min;//总流量累加
}
return ;
}
/********************板子到此结束,以上为最小费用最大流的模板*********************/
int flag[300][300];
int main()
{
int n;
while(~scanf("%d",&n))
{ init();
int x[1005],y[1005];
int z[1005]={0};
int u=0;
memset(flag,0,sizeof(flag));
for(int i=0; i<n; i++)
{
scanf("%d%d",&x[i],&y[i]);
z[u++]=x[i];
z[u++]=y[i];
}
// printf("%d%d\n",xx[x1[0]],y5[0]);
sort(z,z+u);
int len=unique(z,z+u)-z;
int cx[1005]={0};
int cy[1005]={0};
for(int i=0;i<n;i++)
{
x[i]=lower_bound(z,z+len,x[i])-z+1;
y[i]=lower_bound(z,z+len,y[i])-z+1;
cx[x[i]]++;
cy[y[i]]++;
flag[x[i]][y[i]]=1;
}
source=0; sink=2*len+1;
for(int i=1; i<=len; i++)
{
if(cx[i]) addEdge(source,i,cx[i],0);
if(cy[i]) addEdge(i+len,sink,cy[i],0);
}
for(int i=1;i<=len;i++)
{
for(int j=1;j<=len;j++)
{
if(flag[i][j])addEdge(i,j+len,1,1);
else addEdge(i,j+len,1,0);
}
}
int cost, flow;//最小费用 最大流
MCMF(source, sink, cost, flow);
printf("%d\n",cost) ;
}
}