zoukankan      html  css  js  c++  java
  • 【BZOJ 3876】【AHOI 2014】支线剧情

    http://www.lydsy.com/JudgeOnline/problem.php?id=3876
    这道题每条支线的意思是每条边。。。
    那么每条边的下界设为1就行了。
    这样建出一个DAG,每条边下界为1,上界为正无穷,赋上费用。设1为S。所有点向T连边,下界为0,上界为正无穷,费用为0,表示可以随时退出。答案是这个图中的最小费用可行流。
    最小费用可行流怎么求啊!
    可行流什么的我只会求无源汇的。
    想了好半天才明白该怎么做。。。
    抛弃原来的建图,还是建出一个DAG,每条边下界为1,上界为正无穷,赋上费用。所有非1的点都向1连一条下界为0上界无穷费用为0的边,表示可以随时退出回到1点。
    这样就是无源汇的啦!
    我们要求这个新的图(附加网络)的最小费用可行流。
    可行流我会求(套模板),设超级源S和超级汇T,每个点的入点下界和减去出点下界和,记为di。如果di小于0,从i连边向T,容量为-di;如果di大于0,从S连边向i,容量为di(都是模板的内容~)
    从超级源到超级汇跑最大流,跑出来的就是可行流减去下界的流量。因为题意,所以肯定有解;又因为是DAG,所以可行流就是最小流。
    如果要求最小费用可行流?不断spfa增广就可以实现最小费用了!
    这样对于一条边的流量f=d+g,f为可行流的流量,d为下界,g为附加网络中实际的流量。
    求出的最小费用是(sum_{i∈E}g_i*w_i),并不是我们想要的f!
    怎么办呢?因为所有的d一定会流满,所以直接加上(sum_{i∈E}d_i*w_i)即可!(我好蠢啊,想了一晚上)
    附赠样例图示:

    6
    2 2 1 3 2
    2 4 3 5 4
    2 5 5 6 6
    0
    0
    0
    

    附加网络是介个样子的:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N = 333;
    const int M = N * N;
    const int inf = 0x7fffffff;
    
    struct node {
    	int nxt, to, c, w, from;
    } E[M];
    
    int cnt = 1, point[N];
    
    void ins(int u, int v, int c, int w) {
    	E[++cnt] = (node) {point[u], v, c, w, u}; point[u] = cnt;
    	E[++cnt] = (node) {point[v], u, 0, -w, v}; point[v] = cnt;
    }
    
    bool inq[N];
    int dist[N], pre[N], q[N];
    
    bool spfa(int s, int t) {
    	for (int i = 1; i <= t; ++i) dist[i] = inf;
    	int head = 0, tail = 1, u, v, tt;
    	dist[s] = 0; inq[s] = true; q[1] = s;
    	while (head != tail) {
    		++head; if (head == N) head = 0;
    		u = q[head]; inq[u] = false;
    		for (int i = point[u]; i; i = E[i].nxt)
    			if (E[i].c && dist[v = E[i].to] > (tt = dist[u] + E[i].w)) {
    				dist[v] = tt; pre[v] = i;
    				if (!inq[v]) {
    					inq[v] = true;
    					++tail; if (tail == N) tail = 0;
    					q[tail] = v;
    				}
    			}
    	}
    	return dist[t] != inf;
    }
    
    int MCMF(int s, int t) {
    	int ret = 0;
    	while (spfa(s, t)) {
    		int f = inf, u;
    		for (u = t; u != s; u = E[pre[u]].from) f = min(f, E[pre[u]].c);
    		for (u = t; u != s; u = E[pre[u]].from) E[pre[u]].c -= f, E[pre[u] ^ 1].c += f;
    		ret += dist[t] * f;
    	}
    	return ret;
    }
    
    int n, du[N], S, T, ans = 0;
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) {
    		int tot, bi, ti; scanf("%d", &tot);
    		du[i] -= tot;
    		while (tot--) {
    			scanf("%d%d", &bi, &ti);
    			ins(i, bi, inf, ti);
    			++du[bi]; ans += ti;
    		}
    		if (i != 1) ins(i, 1, inf, 0);
    	}
    	
    	S = n + 1; T = S + 1;
    	for (int i = 1; i <= n; ++i) {
    		if (du[i] > 0) ins(S, i, du[i], 0);
    		if (du[i] < 0) ins(i, T, -du[i], 0);
    	}
    	
    	printf("%d
    ", MCMF(S, T) + ans);
    	return 0;
    }
    

    QAQ终于写完了,那么接下来我们

  • 相关阅读:
    Python基础——内置函数
    HHKB Mac快捷键使用
    解决EditText在ListView Item中,第一次点击无法获取焦点问题
    Android 设置Spinner默认显示文字
    IOS-静态Cell
    android-创建流式布局,并修改最后一行的最后一个view
    最简单的设置ExitText只能输入数字和字母的方法
    Android studio私人常用快捷键(持续更新)
    IOS-NSNotification(通知)
    Android书单(持续更新)
  • 原文地址:https://www.cnblogs.com/abclzr/p/6227689.html
Copyright © 2011-2022 走看看