网络流练习题 猜测

猜测
【问题描述】
有一块棋盘,棋盘的边长为 100000,行和列的编号为 1 到 100000。棋盘上
有𝑛个特殊格子,任意两个格子的位置都不相同。
现在小 K 要猜哪些格子是特殊格子。她知道所有格子的横坐标和纵坐标,
但并不知道对应关系。换言之,她只有两个数组,一个存下了所有格子的横坐标,
另一个存下了所有格子的纵坐标,而且两个数组都打乱了顺序。当然,小 K 猜
的𝑛个格子的位置也必须都不相同。
请求出一个最大的𝑘,使得无论小 K 怎么猜,都能猜对至少𝑘个格子的位置。
【输入格式】
输入数据第一行包含一个整数𝑛。
接下来𝑛行,每行描述一个特殊格子的位置。第𝑖行含有两个整数𝑥 ( 和𝑦 𝑖 ,代
表第𝑖个格子的坐标。保证任意两个格子的坐标都不相同。
【输出格式】
输出一行,包含一个整数,代表最大的𝑘。
【样例输入 1】
2
1 1
2 2
【样例输出 1】
0
【样例解释 1】
小 K 有可能会猜(1,2),(2,1),此时一个都没对。
【样例输入 2】
3
1 1
1 2
2 1

【样例输出 2】
3
【样例解释 2】
此时只有一种合法的猜测。注意(1,1),(1,1),(2,2)不是一个合法的猜测。
【数据规模和约定】
对于 30%的数据,𝑛 ≤ 8。
另外有 5%的数据,所有横坐标和纵坐标均不相同。
另外有 15%的数据,所有横坐标或者纵坐标均不相同。
对于 100%的数据,𝑛 ≤ 50,1 ≤ 𝑥i,𝑦i ≤ 100000。

分析:将横坐标和纵坐标匹配,很容易就能看出这道题要用费用流来求解.

   任意一对点的坐标都不相同,并且横坐标的值的数量和纵坐标的值的数量是固定的. 这些都可以通过边的容量来限制.

   坐标比较大,需要先离散化,在值域上跑最小费用最大流.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

const int maxn = 100010,inf = 0x7fffffff;

int n,sum1[maxn],sum2[maxn],a[maxn],b[maxn],c[maxn],cnt,flag[1010][1010];
int S,T,ans,vis[maxn],vis2[maxn],d[maxn],head[maxn],to[maxn],nextt[maxn],w[maxn],cost[maxn],tot = 2;

void add(int x,int y,int z,int p)
{
    cost[tot] = p;
    w[tot] = z;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;

    cost[tot] = -p;
    w[tot] = 0;
    to[tot] = x;
    nextt[tot] = head[y];
    head[y] = tot++;
}

bool spfa()
{
    memset(vis,0,sizeof(vis));
    memset(vis2,0,sizeof(vis2));
    for (int i = 1; i <= T; i++)
        d[i] = inf;
    queue <int> q;
    q.push(S);
    d[S] = 0;
    vis[S] = 1;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (w[i] && d[v] > d[u] + cost[i])
            {
                d[v] = d[u] + cost[i];
                if (!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return d[T] < inf;
}

int dfs(int u,int f)
{
    if (u == T)
    {
        ans += d[u] * f;
        return f;
    }
    int res = 0;
    vis2[u] = 1;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (w[i] && !vis2[v] && d[v] == d[u] + cost[i])
        {
            int temp = dfs(v,min(f - res,w[i]));
            w[i] -= temp;
            w[i ^ 1] += temp;
            res += temp;
            if (res == f)
                return res;
        }
    }
    return res;
}

void dinic()
{
    while (spfa())
        dfs(S,inf);
}

int main()
{
    scanf("%d",&n);
    S = 4 * n + 1;
    T = S + 1;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        c[++cnt] = a[i];
        c[++cnt] = b[i];
    }
    sort(c + 1,c + 1 + cnt);
    cnt = unique(c + 1,c + 1 + cnt) - c - 1;
    for (int i = 1; i <= n; i++)
    {
        a[i] = lower_bound(c + 1,c + 1 + cnt,a[i]) - c;
        b[i] = lower_bound(c + 1,c + 1 + cnt,b[i]) - c;
        sum1[a[i]]++;
        sum2[b[i]]++;
        flag[a[i]][b[i]] = 1;
    }
    for (int i = 1; i <= cnt; i++)
    {
        if (sum1[i])
            add(S,i,sum1[i],0);
        if (sum2[i])
            add(i + 2 * n,T,sum2[i],0);
    }
    for (int i = 1; i <= cnt; i++)
        for (int j = 1; j <= cnt; j++)
        {
            if (flag[i][j])
                add(i,j + 2 * n,1,1);
            else
                add(i,j + 2 * n,1,0);
        }
    dinic();
    printf("%d\n",ans);

    return 0;
}

 

posted @ 2018-03-22 19:45  zbtrs  阅读(451)  评论(0编辑  收藏  举报