HihoCoder 1252 Kejin Game(最小割+点在割的一侧有花费建图)

87 篇文章 0 订阅

Nowadays a lot of Kejin games (the games which are free to get and play, but some items or characters are unavailable unless you pay for it) appeared. For example, Love Live, Kankore, Puzzle & Dragon, Touken Ranbu and Kakusansei Million Arthur (names are not listed in particular order) are very typical among them. Their unbelievably tremendous popularity has become a hot topic, and makes considerable profit every day. You are now playing another Kejin game. In this game, your character has a skill graph which decides how can you gain skills. Particularly speaking, skill graph is an oriented graph, vertices represent skills, and arcs show their relationship — if an arc from A to B exists in the graph (i.e. B has a dependency on A), you need to get skill A before you are ready to gain skill B. If a skill S has more than one dependencies, they all need to be got firstly in order to gain S. Note that there is no cycles in the skill graph, and no two same arcs. Getting a skill takes time and energy, especially for those advanced skills appear very deep in the skill graph. However, as an RMB player, you know that in the game world money could distort even basic principles. For each arc in skill graph, you can “Ke” (which means to pay) some money to erase it. Further, for each skill, you could even “Ke” a sum of money to gain it directly in defiance of any dependencies! As you have neither so much leisure time to get skills nor sufficient money, you decide to balance them. All costs, including time, energy or money, can be counted in the unit “TA”. You calculate costs for all moves (gaining a skill in normal way, erasing an arc and gaining a skill directly). Note that all costs are non-negative integers. Then, you want to know the minimum cost to gain a particular skill S if you haven’t get any skills initially. Solve this problem to make your game life more joyful and … economical.

Input
The input consists of no more than 10 test cases, and it starts with a single integer indicating the number of them. The first line of each test case contains 3 positive integers N (1 ≤ N ≤ 500), M (1 ≤ M ≤ 10000) and S, representing the number of vertices and arcs in the skill graph, and the index of the skill you’d like to get. Vertices are indexed from 1 to N, each representing a skill. Then M lines follow, and each line consists of 3 integers A, B and C, indicating that there is an arc from skill A to skill B, and C (1 ≤ C ≤ 1000000) TAs are needed to erase this arc. The next line contains N integers representing the cost to get N skills in normal way. That means, the i-th integer representing the cost to get the i-th skill after all its dependencies are handled. The last line also contains N integers representing the cost to get N skills directly by “Ke”. These 2N integers are no more than 1000000.

Output
For each test case, output your answer, the minimum total cost to gain skill S, in a single line.

Sample Input
2
555
125
135
248
4 5 10
3 5 15
3 5 7 9 11
100 100 100 200 200
555
125
135
248
4 5 10
3 5 15
3 5 7 9 11
5 5 5 50 50
Sample Output
31
26

题目大意

  有一个DAG,每个点表示一个技能,要先学习指向这个点的所有技能才能学习它,学习每个技能都有一个花费。但是通过氪金,你可以删除边,删除每一条边都有一个花费,也可以直接学习一个技能,每个技能也都有一个花费。问要学会某个特定技能最小的花费。

解题思路

  很明显我们就是要找一个割,目标技能一边(后面叫右边)的点的就是要学习的技能,割边就是花费。对于氪金学习技能,我们需要把点拆成一个入点一个出点,再连一条容量为花费的边。对于氪金删边,我们只需要按照原来的方式连边即可,容量为花费。对于正常学习,由于每个在右边的点都要花费正常学习它的价格,所以我们可以对每个点连一条从源点到它的边,容量为花费。这样最小割即为答案。

AC代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
#include <ctime>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))
#define sqr(x) ((x)*(x))

const int MAXV=10000+3;

struct Edge
{
    int to, cap, rev;
    Edge(int t, int c, int r):to(t), cap(c), rev(r){}
};

vector<Edge> G[MAXV];//图的邻接表表示
int level[MAXV];//顶点到原点的距离标号
int iter[MAXV];//当前弧,在其之前的边已经没有用了
int V;//顶点数

//有向图中增加一条从from到to的容量为cap的边
void add_edge(int from, int to, int cap)
{
    G[from].push_back(Edge(to, cap, G[to].size()));
    G[to].push_back(Edge(from, 0, G[from].size()-1));
}

//通过bfs计算从源点出发的距离标号
void bfs(int s)
{
    for(int i=0;i<V;++i)
        level[i]=-1;
    queue<int> que;
    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int u=que.front(); que.pop();
        for(int i=0;i<G[u].size();++i)
        {
            Edge &e=G[u][i];
            if(e.cap>0 && level[e.to]<0)
            {
                level[e.to]=level[u]+1;
                que.push(e.to);
            }
        }
    }
}

//通过dfs寻找增广路
int dfs(int u, int t, int f)
{
    if(u==t)
        return f;
    for(int &i=iter[u];i<G[u].size();++i)
    {
        Edge &e=G[u][i];
        if(e.cap>0 && level[u]<level[e.to])
        {
            int d=dfs(e.to, t, min(f, e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

//求解从s到t的最大流
int dinic(int s, int t)
{
    int flow=0;
    while(true)
    {
        bfs(s);
        if(level[t]<0)
            return flow;
        for(int i=0;i<V;++i)
            iter[i]=0;
        int f;
        while((f=dfs(s, t, INF))>0)
            flow+=f;
    }
}

int N, M, T;

void init()
{
    V=N*2+1;
    for(int i=0;i<=V;++i)
    {
        G[i].clear();
    }
}

int main()
{
    // 0          源
    // 1 ~ N      入点
    // N+1 ~ N*2  出点
    int T_T;
    scanf("%d", &T_T);
    while(T_T--)
    {
        scanf("%d%d%d", &N, &M, &T);
        init();
        int s=0, t=T;
        for(int i=0;i<M;++i)
        {
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            add_edge(u+N, v, c);
        }
        for(int i=1;i<=N;++i)
        {
            int tmp;
            scanf("%d", &tmp);
            add_edge(s, i, tmp);
        }
        for(int i=1;i<=N;++i)
        {
            int tmp;
            scanf("%d", &tmp);
            add_edge(i, i+N, tmp);
        }
        printf("%d\n", dinic(s, t));
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值