师范大学の矿山

故事扮演 提交于 2019-11-27 16:42:42

师范大学の矿山 


Time Limit: 1000/2000 MS (C++/Others) Memory Limit: 262144 /524288K (C++/Others)
题目描述 #
师范大学有一座矿山,因为改造江边学生公寓需要花费大量资金,所以现在让你负 责挖矿,你需要根据矿石(以下称石头)的属性来计算它能获得的最大利润。
每一块石头  有一个价值  和挖掘的成本  ,有些石头会阻挡其他石头,比如,如 果石头  被石头  和石头  挡住,那么必须先把石头  和石头  挖出来,才能挖石头 ,当一块石头没有被其他石头阻挡时,它就可以被挖出来。
输入描述 # 第一行一个整数  ,代表石头的数量,石头编号从  到  ,接下来  行描述这些 石头的属性。第  行代表石头  ,首先是两个数  和  代表价值和成本,然后第三 个数  表示石头  阻挡的石头数量,最后是  个数 表示石头  阻挡的那些石头的 编号。
输入保证有合理的挖掘顺序来挖掘每一块石头,所有石头的  之和不会超过500。

输出描述 #1<=N<=200,0<=vi,ci<=200,0<=mi<=N-1
输出一个整数代表挖掘这些石头能获取的最大利润。
输入
5

0 3 2 2 3

1 3 2 4 5

4 8 1 4

5 3 0

9 2 0
输出 

 

题解:先把所有能获利 的点都加起来,再建图(从源点到获利的点连边,权值为获利值,把赔钱的点到汇点连边,权值为赔钱数值,如果在挖第i个点之前要先挖第J个点,那么从第i个点到第j个点连边,权值为INF),找最大流,即最大流的值为要选择这些获利点必须要损失的钱,最后把所有获利点的利润加起来然后减去最大流,即为能得到的最大利润。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 510;
const int maxm = 250010;
int N, NP, NC;
const int inf = 0x3f3f3f3f;
struct Edge
{
    int u, v, cap;
    Edge() {}
    Edge(int u, int v, int cap): u(u), v(v), cap(cap) {}
} es[maxm];
int R, S, T;
vector<int> tab[maxn]; // 边集
int dis[maxn];
int current[maxn];
void addedge(int u, int v, int cap)
{
    tab[u].push_back(R);
    es[R++] = Edge(u, v, cap); // 正向边
    tab[v].push_back(R);
    es[R++] = Edge(v, u, 0); // 反向边容量为0
    // 正向边下标通过异或就得到反向边下标, 2 ^ 1 == 3 ; 3 ^ 1 == 2
}
int BFS()
{
    queue<int> q;
    q.push(S);
    memset(dis, 0x3f, sizeof(dis));
    dis[S] = 0;
    while (!q.empty())
    {
        int h = q.front();
        q.pop();
        for (int i = 0; i < tab[h].size(); i++)
        {
            Edge &e = es[tab[h][i]];
            if (e.cap > 0 && dis[e.v] == 0x3f3f3f3f)
            {
                dis[e.v] = dis[h] + 1;
                q.push(e.v);
            }
        }
    }
    return dis[T] < 0x3f3f3f3f; // 返回是否能够到达汇点
}
int dinic(int x, int maxflow)
{
    if (x == T)
        return maxflow;
    // i = current[x] 当前弧优化
    for (int i = current[x]; i < tab[x].size(); i++)
    {
        current[x] = i;
        Edge &e = es[tab[x][i]];
        if (dis[e.v] == dis[x] + 1 && e.cap > 0)
        {
            int flow = dinic(e.v, min(maxflow, e.cap));
            if (flow)
            {
                e.cap -= flow; // 正向边流量降低
                es[tab[x][i] ^ 1].cap += flow; // 反向边流量增加
                return flow;
            }
        }
    }
    return 0; // 找不到增广路 退出
}

int DINIC()
{
    int ans = 0;
    while (BFS()) // 建立分层图
    {
        int flow;
        memset(current, 0, sizeof(current)); // BFS后应当清空当前弧数组
        while (flow = dinic(S, 0x3f3f3f3f)) // 一次BFS可以进行多次增广
            ans += flow;
    }
    return ans;
}

int main()
{
    int n,u, v, m, x;
    scanf("%d", &n);
    R = 0, S = 0, T = n + 1;
    int sum = 0;
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d%d%d", &u, &v, &m);
        if (u > v)
        {
            addedge(S, i, u - v); // 土地与源点
            sum += (u - v);
        }
        else
            addedge(i, T, v - u); // 土地与汇点
        for (int j = 0; j < m; ++j)
        {
            scanf("%d", &x);
            addedge(x, i, inf);
        }
    }
    printf("%d\n", sum - DINIC());
    return 0;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!