zoukankan      html  css  js  c++  java
  • BZOJ2324 [ZJOI2011]营救皮卡丘 【费用流】

    题目

    皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。
    火箭队一共有N个据点,据点之间存在M条双向道路。据点分别从1到N标号。小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘。为了方便起见,我们将真新镇视为0号据点,一开始K个人都在0号点。
    由于火箭队的重重布防,要想摧毁K号据点,必须按照顺序先摧毁1到K-1号据点,并且,如果K-1号据点没有被摧毁,由于防御的连锁性,小智一行任何一个人进入据点K,都会被发现,并产生严重后果。因此,在K-1号据点被摧毁之前,任何人是不能够经过K号据点的。
    为了简化问题,我们忽略战斗环节,小智一行任何一个人经过K号据点即认为K号据点被摧毁。被摧毁的据点依然是可以被经过的。
    K个人是可以分头行动的,只要有任何一个人在K-1号据点被摧毁之后,经过K号据点,K号据点就被摧毁了。显然的,只要N号据点被摧毁,皮卡丘就得救了。
    野外的道路是不安全的,因此小智一行希望在摧毁N号据点救出皮卡丘的同时,使得K个人所经过的道路的长度总和最少。
    请你帮助小智设计一个最佳的营救方案吧!

    输入格式

    第一行包含三个正整数N,M,K。表示一共有N+1个据点,分别从0到N编号,以及M条无向边。一开始小智一行共K个人均位于0号点。
    接下来M行,每行三个非负整数,第i行的整数为Ai,Bi,Li。表示存在一条从Ai号据点到Bi号据点的长度为Li的道路。

    输出格式

    仅包含一个整数S,为营救皮卡丘所需要经过的最小的道路总和。

    输入样例

    3 4 2

    0 1 1

    1 2 1

    2 3 100

    0 3 1

    输出样例

    3

    【样例说明】

    小智和小霞一起前去营救皮卡丘。在最优方案中,小智先从真新镇前往1号点,接着前往2号据点。当小智成功摧毁2号据点之后,小霞从真新镇出发直接前往3号据点,救出皮卡丘。

    提示

    对于100%的数据满足N ≤ 150, M ≤ 20 000, 1 ≤ K ≤ 10, Li ≤ 10 000, 保证小智一行一定能够救出皮卡丘。至于为什么K ≤ 10,你可以认为最终在小智的号召下,小智,小霞,小刚,小建,小遥,小胜,小光,艾莉丝,天桐,还有去日本旅游的黑猫警长,一同前去大战火箭队。

    题解

    比较容易想到,每一次的移动,一定是为了到达一个未摧毁的点,也就是一个比当前点都大的点
    所以每一次移动时,路径上点点都不会比终点大

    由此,我们可以floyd预处理两点间最短路,但用以更新的k不能比i和j都大

    这样我们重建了一张竞赛图,也是一张拓扑图
    现在问题就转化成了:选尽量短的几条从0出发的路径,使所有点都被覆盖

    就有转化为了最小费用可行流

    具体怎么建图,就不说了【懒。】

    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 310,maxm = 500005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
    	return out * flag;
    }
    int n,m,K,h[maxn],ne = 2,S,T,SS,TT;
    struct EDGE{int to,nxt,f,w;}ed[maxm];
    inline void build(int u,int v,int f,int w){
    	ed[ne] = (EDGE){v,h[u],f,w}; h[u] = ne++;
    	ed[ne] = (EDGE){u,h[v],0,-w}; h[v] = ne++;
    }
    int d[maxn],minf[maxn],p[maxn],inq[maxn];
    queue<int> q;
    int mincost(){
    	int flow = 0,cost = 0;
    	while (true){
    		for (int i = 0; i <= TT; i++) d[i] = INF,inq[i] = 0;
    		d[SS] = 0; minf[SS] = INF;
    		q.push(SS);
    		int u;
    		while (!q.empty()){
    			u = q.front(); q.pop();
    			inq[u] = false;
    			Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){
    				d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);
    				if (!inq[to]) q.push(to),inq[to] = true;
    			}
    		}
    		if (d[TT] == INF) break;
    		flow += minf[TT];
    		cost += minf[TT] * d[TT];
    		u = TT;
    		while (u != SS){
    			ed[p[u]].f -= minf[T];
    			ed[p[u] ^ 1].f += minf[T];
    			u = ed[p[u] ^ 1].to;
    		}
    	}
    	return cost;
    }
    int G[maxn][maxn];
    void floyd(){
    	for (int k = 0; k < n; k++)
    		for (int i = 0; i < n; i++)
    			for (int j = 0; j < n; j++)
    				if ((k <= i || k <= j) && G[i][j] > G[i][k] + G[k][j])
    					G[i][j] = G[i][k] + G[k][j];
    }
    void init(){
    	for (int i = 0; i < n; i++){
    		build(SS,i + n,1,0);
    		build(i,TT,1,0);
    		build(i,i + n,INF,0);
    		build(i + n,T,INF,0);
    	}
    	build(S,0,K,0);
    	build(T,S,INF,0);
    	for (int i = 0; i < n; i++)
    		for (int j = i + 1; j < n; j++)
    			build(i + n,j,INF,G[i][j]);
    }
    int main(){
    	n = read(); m = read(); K = read(); n++;
    	S = 2 * n; T = S + 1; SS = T + 1; TT = SS + 1;
    	int a,b,w;
    	for (int i = 0; i < n; i++)
    		for (int j = 0; j < n; j++)
    			if (i != j) G[i][j] = INF;
    	while (m--){
    		a = read(); b = read(); w = read();
    		G[a][b] = G[b][a] = min(G[a][b],w);
    	}
    	floyd();
    	init();
    	printf("%d
    ",mincost());
    	return 0;
    }
    
    
  • 相关阅读:
    电工知识:3种方法测电容的好坏,万用表三个档位的巧妙应用
    ps 教程
    绘声绘影 设置不联网
    推荐.Net、C# 逆向反编译四大工具利器
    MOOC 网站:Coursera、Udacity、edX
    深度强化学习资料(视频+PPT+PDF下载)
    李飞飞、吴恩达、Bengio等人的15大顶级深度学习课程
    tf.name_scope()和tf.variable_scope()
    Linux 进程(一):环境及其控制
    Linux I/O总结
  • 原文地址:https://www.cnblogs.com/Mychael/p/8449288.html
Copyright © 2011-2022 走看看