暑假集训专题练习记录———网络流

目录

这次的题目没有写完,虽然题目很少吧……以后机会再补上

A ACM Computer Factory(POJ 3436)

B Dining(POJ 3281)

题目大意:
有n头牛,f种食物,d种饮料。每头牛有自己喜欢的食物和饮料。输出能让牛吃到自己喜欢的食物和喝到自己喜欢饮料的数量。
思路:
最大流。
建图方面:
上图。其中容量都是1。然后直接套dinic就行了。
在这里插入图片描述AC代码

#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;

const int max_n = 400 + 100;

int res[max_n][max_n];
int dis[max_n];
int source, sink, n, maxflow;

void add(int from, int to, int cap)
{
    res[from][to] = cap;
}

int bfs()
{
    int k;
    queue<int> q;
    memset(dis, -1, sizeof(dis));
    dis[sink] = 0;

    q.push(sink);
    while (!q.empty())
    {
        k = q.front();
        q.pop();
        for (int i = 0; i < n; i++)
        {
            if (dis[i] == -1 && res[i][k])
            {
                dis[i] = dis[k] + 1;
                q.push(i);
            }
        }
        if (k == source)
            return 1;
    }
    return 0;
}

int dfs(int cur, int cp)
{
    if (cur == sink)
        return cp;

    int tmp = cp, t;
    for (int i = 0; i < n && tmp; i++)
    {
        if (dis[i] + 1 == dis[cur] && res[cur][i])
        {
            t = dfs(i, min(res[cur][i], tmp));
            res[cur][i] -= t;
            res[i][cur] += t;
            tmp -= t;
        }
    }
    return cp - tmp;
}

void dinic()
{
    maxflow = 0;
    while (bfs())
        maxflow += dfs(source, 1 << 30);
}

int main()
{
    int f, d;
    int ff, dd, tmp;
    cin >> n >> f >> d;
    //源点 汇点
    source = 0, sink = 2 * n + f + d + 1;

    //源点到食物
    for (int i = 1; i <= f; i++)
        add(source, 2 * n + i, 1);
    //饮料到汇点
    for (int i = 1; i <= d; i++)
        add(2 * n + f + i, sink, 1);
    //食物牛到饮料牛
    for (int i = 1; i <= n; i++)
        add(i, n + i, 1);

    for (int i = 1; i <= n; i++)
    {
        cin >> ff >> dd;

        for (int j = 1; j <= ff; j++)
        {
            //食物到食物牛
            cin >> tmp;
            add(2 * n + tmp, i, 1);
        }
        for (int j = 1; j <= dd; j++)
        {
            //饮料牛到饮料
            cin >> tmp;
            add(i + n, 2 * n + f + tmp, 1);
        }
    }

    n = sink + 1; //代表总的点数了

    dinic();
    cout << maxflow << endl;

    return 0;
}

C A Plug for UNIX(POJ 1087)

题目大意:
有一些插座,还有一些设备的插头,要给设备充电,还会给出某些插座可以互相插,问最少有多少个不被充。
思路:
就是求最多有多少个被充的,然后拿总数去减就行了。
建图方面:
1、插座到汇点的容量为 1
2、源点到设备的容量为 1
3、设备到插座的容量为 1
4、插座到插座的容量为 INF

使用map去存字符串,然后使用index下标讲所有字符串都转换成数字。然后直接跑dinic就行了。
AC代码

#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;

const int max_n = 800 + 100;

int res[max_n][max_n];
int dis[max_n];
int source, sink, n, maxflow;

void add(int from, int to, int cap)
{
    res[from][to] = cap;
}

int bfs()
{
    int k;
    queue<int> q;
    memset(dis, -1, sizeof(dis));
    dis[sink] = 0;

    q.push(sink);
    while (!q.empty())
    {
        k = q.front();
        q.pop();
        for (int i = 0; i < n; i++)
        {
            if (dis[i] == -1 && res[i][k])
            {
                dis[i] = dis[k] + 1;
                q.push(i);
            }
        }
        if (k == source)
            return 1;
    }
    return 0;
}

int dfs(int cur, int cp)
{
    if (cur == sink)
        return cp;

    int tmp = cp, t;
    for (int i = 0; i < n && tmp; i++)
    {
        if (dis[i] + 1 == dis[cur] && res[cur][i])
        {
            t = dfs(i, min(res[cur][i], tmp));
            res[cur][i] -= t;
            res[i][cur] += t;
            tmp -= t;
        }
    }
    return cp - tmp;
}

void dinic()
{
    maxflow = 0;
    while (bfs())
        maxflow += dfs(source, 1 << 30);
}

map<string, int> mp; //字母标号

int main()
{
    cin >> n;

    source = 0, sink = max_n - 1;
    int index = 1;
    //插座到汇点
    for (int i = 0; i < n; i++)
    {
        string ch;
        cin >> ch;
        mp[ch] = index++;
        add(mp[ch], sink, 1);
    }
    int f;
    cin >> f;

    for (int i = 0; i < f; i++)
    {
        string s, ch;
        cin >> s >> ch;
        if (!mp[s])
            mp[s] = index++;
        //源点到设备
        add(source, mp[s], 1);

        if (!mp[ch])
            mp[ch] = index++;
        // 设备到插座
        add(mp[s], mp[ch], 1);
    }

    int m;
    cin >> m;
    //插座到插座
    for (int i = 0; i < m; i++)
    {
        string a, b;
        cin >> a >> b;
        if (!mp[a])
            mp[a] = index++;
        if (!mp[b])
            mp[b] = index++;

        add(mp[a], mp[b], 1 << 30);
    }

    n = max_n;

    dinic();
    cout << f - maxflow << endl;

    return 0;
}

D Going Home(POJ 2195)

题目大意:
有一个地图,m是人,h是家,人要回家,一个家只能待一个人,一个人动一步要一块钱,问最小费用。
思路:
最小费用流。
建图方面:
1、找出所有人的坐标和所有家的坐标。
2、对于每一个家计算出所有人到这个家的价格(曼哈顿距离)当作费用,容量为1
3、将所有家连入源点,费用为0,容量为1
4、将所有的人连入汇点,费用为0,容量为1

建好图之后直接跑最小费用流算法就行了。
AC代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>

using namespace std;

#define INF 0x3f3f3f3f
#define ll long long
#define endl "\n"
const int max_n = 1e4 + 20;

typedef pair<int, int> P;

struct edge
{
    //终点 容量 费用 反向边
    int to, cap, cost, rev;
};

int V;
vector<edge> G[max_n];
int h[max_n];
int dist[max_n];
int prevv[max_n], preve[max_n];

void add(int from, int to, int cap, int cost)
{
    G[from].push_back((edge){to, cap, cost, (int)G[to].size()});
    G[to].push_back((edge){from, 0, -cost, (int)G[from].size() - 1});
}

int min_cost_flow(int s, int t)
{
    int res = 0;

    fill(h, h + V, 0);
    while (true)
    {
        priority_queue<P, vector<P>, greater<P>> que;
        fill(dist, dist + V, INF);
        dist[s] = 0;
        que.push(P(0, s));

        while (!que.empty())
        {
            P p = que.top();
            que.pop();
            int v = p.second;
            if (dist[v] < p.first)
                continue;
            for (int i = 0; i < G[v].size(); i++)
            {
                edge &e = G[v][i];
                if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to])
                {
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push(P(dist[e.to], e.to));
                }
            }
        }
        if (dist[t] == INF)
            break;
        for (int v = 0; v < V; v++)
            h[v] += dist[v];

        int d = INF;
        for (int v = t; v != s; v = prevv[v])
        {
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        if (d == 0)
            break;
        res += d * h[t];
        for (int v = t; v != s; v = prevv[v])
        {
            edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

string s[max_n];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n, m;
    while (cin >> n >> m)
    {
        if (n == 0 && m == 0)
            break;

        for (int i = 0; i < n; i++)
            cin >> s[i];

        vector<P> hh, mm;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                if (s[i][j] == 'H')
                    hh.push_back(make_pair(i, j));
                if (s[i][j] == 'm')
                    mm.push_back(make_pair(i, j));
            }
        }

        V = 2 * hh.size() + 2;
        int s = 2 * hh.size();
        int t = s + 1;
        for (int i = 0; i < V + 10; i++)
            G[i].clear();
        for (int i = 0; i < hh.size(); i++)
        {
            for (int j = 0; j < hh.size(); j++)
                add(i, hh.size() + j, 1, abs(mm[i].first - hh[j].first) + abs(mm[i].second - hh[j].second));
        }

        for (int i = 0; i < hh.size(); i++)
        {
            add(s, i, 1, 0);
            add(hh.size() + i, t, 1, 0);
        }
        cout << min_cost_flow(s, t) << endl;
    }

    return 0;
}

E Minimum Cost(POJ 2516)

F Power Network(POJ 1459)

总结

网络流部分,感觉建图是最难的,虽然很多题没有写出来,主要是因为题目读完了,算法也知道用啥了,但是完全不知道怎么建图,,,感觉建好图,这个题目就可以解决80%了。
剩下的几道题目,以后对网络流的理解更深了之后再去写吧,现在也就只会套板子去做,感觉很多细节方面都不知道怎么整的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值