poj 1087

题目概述

一间屋子里有N种插座,每种插座有唯一的名称,且数量只有一个,在这个屋里可能会要使用M种电器,每种电器名称也是唯一,而且也只有一个,需要一种插座,市面上有K种插座适配器,其需要一个插座,同时也提供一个插座,数量不限,问最少有多少电器会因没有插座无法使用
某些电器需要的插座可能屋里根本没有

时限

1000ms/3000ms

输入

第一行整数N,其后N行,每行一个字符串,代表一种现有插座的名称,下一行整数M,其后M行,每行两个字符串,电器名称和需要的插座名称,下一行整数K,其后K行,每行两个字符串,为适配器提供的插座和需要的插座,输入只有一组

限制

1<=N,M,K<=100;1<=字符串长度<=24;字符串只含字母和数字

输出

一个数,为所求最少无法使用数

样例输入

4
A
B
C
D
5
laptop B
phone C
pager B
clock B
comb X
3
B X
X A
X D

样例输出

1

讨论

图论,网络流,最大流,Isap+gap优化,原题有点长,有些信息就显得很隐蔽,构图不难,源点,电器,插座,汇点,除了适配器以外其他边残量都是1,适配器则是从一种插座到另一种插座连边,残量无穷大,基本就是这样
实现方面,先说输出,问的是有多少没电的,所以要用M减掉最大流量,其次,数据范围,原有插座100,电器100,够了?不够,如果所有电器需要的插座都不存在呢,又是100,所以需要300才够,字符串处理用map映射string到int即可
谁能告诉额为啥梳子需要插电……

题解状态

624K,32MS,C++,1894B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<iostream>
#include<string>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 304
#define memset0(a) memset(a,0,sizeof(a))

int N, M, K, index, S, T = 301;//原有插座数 电器数 适配器种类 每个节点分配的编号 源点 汇点 汇点不是301 后面重新赋值了
map<string, int>plugs;//记录所有节点的编号
int R[MAXN][MAXN], dis[MAXN], from[MAXN], gap[MAXN];//最大流四数组 残量矩阵 层次 父节点 每个层次的节点数
string name1, name2;//用于临时存放字符串
queue<int>q;
int fun()
{
    for (int p = 0; p < N; p++) {
        cin >> name1;//input
        plugs[name1] = ++index;//先为插座分配编号 没有构图 因为不知道M和K的值不便于分配汇点编号
    }
    scanf("%d", &M);//input//感谢cin的输入流同步 即便混用也不会出错
    for (int p = 0; p < M; p++) {
        cin >> name1 >> name2;//input
        R[S][plugs[name1] = ++index] = 1;//源点到电器 残量1
        if (plugs.find(name2) == plugs.end())
            R[plugs[name1]][plugs[name2] = ++index] = 1;//对不存在的插座要额外读入map 这是从电器到插座的边
        else
            R[plugs[name1]][plugs[name2]] = 1;
    }
    scanf("%d", &K);//input
    for (int p = 0; p < K; p++) {
        cin >> name1 >> name2;//input
        R[plugs[name1]][plugs[name2]] = INF;//适配器的插座到插座流量无穷 因为有无限供应
    }
    T = (++index)++;//为汇点分配编号 这里index和以前题里的N一样
    for (int p = 1; p <= N; p++)
        R[p][T] = 1;//插座到汇点的边 由于最先读入 因此一定在这个范围内 注意电器带来的新插座类型不能连到汇点 因为他们并不存在
    for (int p = 0; p < index; p++)//往后就是常用的isap算法了 不多解释了
        dis[p] = index;
    gap[dis[T] = 0]++;
    q.push(T);
    while (!q.empty()) {
        int a = q.front();
        q.pop();
        for (int p = 0; p < index; p++)
            if (dis[p] == index&&R[p][a]) {
                q.push(p);
                gap[dis[p] = dis[a] + 1]++;
            }
    }
    int most = 0, p = S, flow = INF;
    while (dis[S] < index) {
        int i;
        for (i = 0; i < index && (!R[p][i] || dis[p] != dis[i] + 1); i++);
        if (i != index) {
            from[i] = p;
            flow = min(flow, R[p][i]);
            p = i;
            if (p == T) {
                while (p != S) {
                    i = from[p];
                    R[i][p] -= flow;
                    R[p][i] += flow;
                    p = i;
                }
                most += flow;
                flow = INF;
            }
        }
        else {
            if (!--gap[dis[p]])
                break;
            dis[p] = index;
            for (int i = 0; i < index; i++)
                if (R[p][i] && dis[p] > dis[i] + 1)
                    dis[p] = dis[i] + 1;
            gap[dis[p]]++;
            if (p != S)
                p = from[p];
        }
    }
    return M - most;//不要想错 这里返回的是M减最大流量
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    scanf("%d", &N);//input
    printf("%d\n", fun());//output
}

EOF

展开阅读全文
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值