zoukankan      html  css  js  c++  java
  • UVA

    1.A - A Plug for UNIX

    题意:

    有n个插座,m个设备和k种转换器,每种转换器都由无限多。已知每个插座的类型,每个设备的插头类型,以及每种转换器的插头类型和插座类型。插头和插座类型都不超过24个字母表示,插头只能插到对应类型名称相同的插座。

    问:要求插得设备尽量多,问最少剩几个不匹配的设备。

    按照紫书上的思路可以有两种解法:

    • 第一种

    首先建图,图的节点表示插头类型,图的边表示转换器。利用floyd算法求出每种插头可以转换的情况(注意转换器是可以有无限多的);
    其次构造网络:设设备i的插头类型为devices[i],插座i对应的插头类型为target[i],源点s到所有的devices[i]连一条弧,容量为1,所有target[i]到汇点t连接一条弧,容量为1;对于设备插座deveces[i]和插座的对应插头类型target[j],如果devices[i]可以转换为target[j],则连接一条弧,容量无限制;这样就转化为求源点s到汇点t的最大流。

    再求最大流的时候,紫书上介绍的是Edmonds-Karp算法的原理,但是比赛一般用的是ISAP和dinic,这两个算法在紫书的训练指南上有介绍,模板可以借鉴匡斌的模板,对于ISAP和dinic可以不详细探究他的原理,当做STL一样的东西会用就行。

    • 第二种
      第二种是把所有的插头类型都标出来,如果存在一条devices[i]到target[j]的转换器,则连接一条弧,容量无穷大。最后也求s-t的最大流
      (第二种方法可能更像是二分图匹配)

    复习一下二分图匹配:
    二分图匹配有两种问题

    1.针对无权图
    求包含边数最多的匹配。(而二分图的最大基数匹配)
    思路是增加源点跟汇点,求s-t的最大流

    2.针对带权图
    求边权和尽量大的匹配
    分为两类:

    • 完美匹配
      每个点都要匹配到

    • 不对边的数量做要求
      只要求最大边权和

    (遗忘了的话,课后自主复习一下)


    解题代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define clr(a,x) memset(a, x, sizeof(a))
    #define mp(x,y) make_pair(x,y)
    #define pb(x) push_back(x)
    #define X first
    #define Y second
    #define fastin ios_base::sync_with_stdio(0);cin.tie(0);
    typedef long long ll;
    typedef long double ld;
    typedef pair<int, int> PII;
    typedef vector<int> VI;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    const double eps = 1e-6;
    
    const int maxn = 405;
    
    struct Edge
    {
        int from, to, cap, flow;
        Edge(int u, int v, int c, int f): from(u), to(v), cap(c), flow(f) {}
    };
    struct Dinic
    {
        int n, m, s, t;         //结点数,边数(包括反向弧),源点编号和汇点编号
        vector<Edge> edges;     //边表。edge[e]和edge[e^1]互为反向弧
        vector<int> G[maxn];    //邻接表,G[i][j]表示节点i的第j条边在e数组中的序号
        bool vis[maxn];         //BFS使用
        int d[maxn];            //从起点到i的距离
        int cur[maxn];          //当前弧下标
        void init(int n)
        {
            this->n = n;
            for (int i = 0; i < n; i++) G[i].clear();
            edges.clear();
        }
        void AddEdge(int from, int to, int cap)
        {
            edges.pb(Edge(from, to, cap, 0));
            edges.pb(Edge(to, from, 0, 0));
            m = edges.size();
            G[from].pb(m - 2);
            G[to].pb(m - 1);
        }
        bool BFS()
        {
            clr(vis, 0);
            clr(d, 0);
            queue<int> q;
            q.push(s);
            d[s] = 0;
            vis[s] = 1;
            while (!q.empty())
            {
                int x = q.front();
                q.pop();
                for (int i = 0; i < G[x].size(); i++)
                {
                    Edge& e = edges[G[x][i]];
                    if (!vis[e.to] && e.cap > e.flow)
                    {
                        vis[e.to] = 1;
                        d[e.to] = d[x] + 1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t];
        }
        int DFS(int x, int a)
        {
            if (x == t || a == 0) return a;
            int flow = 0, f;
            for (int& i = cur[x]; i < G[x].size(); i++)
            {
                //从上次考虑的弧
                Edge& e = edges[G[x][i]];
                if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
                {
                    e.flow += f;
                    edges[G[x][i] ^ 1].flow -= f;
                    flow += f;
                    a -= f;
                    if (a == 0) break;
                }
            }
            return flow;
        }
        int Maxflow(int s, int t)
        {
            this->s = s;
            this->t = t;
            int flow = 0;
            while (BFS())
            {
                clr(cur, 0);
                flow += DFS(s, INF);
            }
            return flow;
        }
    } ans;
    
    map <string, int> M;
    
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("1.in", "r", stdin);
        freopen("1.out", "w", stdout);
    #endif
        fastin
        int T, n, m, k;
        cin >> T;
        while (T--)
        {
            M.clear();
            cin >> n;
            int s = 0, t = 1;
            ans.init(maxn);
            int tot = 2;
            string tmp, dev;
            while (n--)
            {
                //插座->超级汇点
                cin >> tmp;
                if (!M[tmp]) M[tmp] = tot++;
                ans.AddEdge(M[tmp], t, 1);
            }
            cin >> m;
            for (int i = 0; i < m; i++)
            {
                //超级源点->电器
                cin >> dev >> tmp;
                if (!M[tmp]) M[tmp] = tot++;
                ans.AddEdge(s, M[tmp], 1);
            }
            cin >> k;
            string u, v;
            while (k--)
            {
                //转换器
                cin >> u >> v;
                if (!M[u]) M[u] = tot++;
                if (!M[v]) M[v] = tot++;
                ans.AddEdge(M[u], M[v], INF);
            }
            cout << m - ans.Maxflow(s, t) << endl;
            if (T) cout << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    删除链表中的一个节点
    链表系列面试题1
    线程的5种状态
    红黑树 实现
    Java的SPI机制浅析与简单示例
    socket原理
    rabbitmq简单介绍
    MongoTemplate操作mongodb
    RJava配置
    浅析前后台分离
  • 原文地址:https://www.cnblogs.com/bryce1010/p/9386889.html
Copyright © 2011-2022 走看看