UVALive 7264 Kejin Game (最大流最小割)

15 篇文章 0 订阅


题意:有一个技能树,每个技能有一些“先修技能”,先修技能都得到后才可以得到当前技能。三种方法:1.逐个修技能,花费是技能的点值(数据中的倒数第二行)2.花费一些钱去掉某些边 (边权就是先修的条件)  3.跳过之前的条件直接得到某个技能,求得到某技能的最小花费


思路:经典建图。。建完图你会发现最小割就是要求的答案...

 将每个点拆成 i 和 i',设一个源点与汇点。

1.对于边i--->j,连边i'--->j,容量为对应边的费用。

2.源点到 i 连边,容量为满足条件后购买 i 的费用。

3.对于 i 和 i' 连边,容量为直接购买 i 的费用。

4.对于目标点向汇点连边,容量为INF。

因为图中每个割为一种获得目标技能的方案,所以求最小割即可。


一开始知道最小割不就是一个原来联通图,通过去掉一些权值最小边, 变为不联通的图,这题怎么想也想不董根最小割啥关系啊,画了图知道了,这题是通过建边来将答案变成最小割(字比较难看QAQ


你会发现, 1234个割, 每个其实都是一种答案, 2把e点跟他相邻的三个技能点都取消了, 说明他没有前置技能点了,只需要加上平常的花费就好了, 1很简单, 就是从1到e挨个修炼,3,其实是把E的前置技能点全部买断了,再加上到E的花费, 4就是直接把E买断了...将E到t负值INF, 也就是最小割只能在前面的边出现...


总结下:

这题建图方式很强, 通过建图, 将问题转化为最小割问题,这也提示我们, 当有前置条件, 求最大最小的时候, 可能是网络流, 可能要转化成最小割, 这种建图方式要学一下,每个技能都直接练到源点, 单个技能肯定要拆点将它变成一个流, 这题,也许能出是通过画图看出来的, 以后不知道怎么建图,尝试画图,先把一些常规的画出来,往最小割拼拼....

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e3+5;
const int maxv = 1e5 + 5;
int head[maxn], cur[maxn], d[maxn], s, t, k, sum;
int n, m, g;
struct node
{
    int v, w, next;
}edge[maxv];
void addEdge(int u, int v, int w)
{
    edge[k].v = v;
    edge[k].w = w;
    edge[k].next = head[u];
    head[u] = k++;
    edge[k].v = u;
    edge[k].w = 0;
    edge[k].next = head[v];
    head[v] = k++;

}
int bfs()
{
    memset(d, 0, sizeof(d));
    d[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        if(u == t) return 1;
        q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int to = edge[i].v, w = edge[i].w;
            if(w && d[to] == 0)
            {
                d[to] = d[u] + 1;
                if(to == t) return 1;
                q.push(to);
            }
        }
    }
    return 0;
}
int dfs(int u, int maxflow)
{
    if(u == t) return maxflow;
    int ret = 0;
    for(int i = cur[u]; i != -1; i = edge[i].next)
    {
        int to = edge[i].v, w = edge[i].w;
        if(w && d[to] == d[u]+1)
        {
            int f = dfs(to, min(maxflow-ret, w));
            edge[i].w -= f;
            edge[i^1].w += f;
            ret += f;
            if(ret == maxflow) return ret;
        }
    }
    return ret;
}
int Dinic()
{
    int ans = 0;
    while(bfs() == 1)
    {
        memcpy(cur, head, sizeof(head));
        ans += dfs(s, INF);
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &g);
        s = 0, t = n*2+1, k = 0;
        memset(head, -1, sizeof(head));
        int x, y, z;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d", &x, &y, &z);
            addEdge(n+x, y, z);
        }
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &x);
            addEdge(s, i, x);
        }
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &x);
            addEdge(i, i+n, x);
        }
        addEdge(g+n, t, INF);
        printf("%d\n", Dinic());
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值