zoukankan      html  css  js  c++  java
  • 【BZOJ】1927: [Sdoi2010]星际竞速(费用流)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1927

    题意:n个点的无向图。m条加权边。只能从编号小的到编号大的。可以瞬移,瞬移有时间。每个点只能访问一次。问访问所有n个点的最少时间。(N<=800, M<=15000)

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << (#x) << " = " << (x) << endl
    #define error(x) (!(x)?puts("error"):0)
    #define rdm(x, i) for(int i=ihead[x]; i; i=e[i].next)
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    
    const int N=2005, oo=~0u>>2;
    int ihead[N], cnt=1, q[N], n, p[N], d[N], vis[N];
    struct dat { int next, to, cap, from, w; }e[N*N];
    void add(int u, int v, int c, int w) {
    	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v; e[cnt].from=u; e[cnt].cap=c; e[cnt].w=w;
    	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u; e[cnt].from=v; e[cnt].cap=0; e[cnt].w=-w;
    }
    bool spfa(int s, int t) {
    	for1(i, 0, t) vis[i]=0, d[i]=oo;
    	d[s]=0; int front=0, tail=0;
    	q[tail++]=s;
    	while(front!=tail) {
    		int u=q[front++], v; if(front==N) front=0; vis[u]=0;
    		rdm(u, i) if(e[i].cap) {
    			v=e[i].to;
    			if(d[v]>d[u]+e[i].w) {
    				d[v]=d[u]+e[i].w;
    				p[v]=i;
    				if(!vis[v]) {
    					vis[v]=1;
    					if(d[v]<d[q[front]]) {
    						--front; if(front<0) front+=N;
    						q[front]=v;
    					}
    					else {
    						q[tail++]=v; if(tail==N) tail=0;
    					}
    				}
    			}
    		}
    	}
    	return d[t]!=oo;
    }
    int mcf(int s, int t) {
    	int ret=0, f, u;
    	while(spfa(s, t)) {
    		f=oo;
    		for(u=t; u!=s; u=e[p[u]].from) f=min(f, e[p[u]].cap);
    		for(u=t; u!=s; u=e[p[u]].from) e[p[u]].cap-=f, e[p[u]^1].cap+=f;
    		ret+=f*d[t];
    	}
    	return ret;
    }
    int main() {
    	read(n);
    	int m=getint();
    	int s=0, t=n+n+1;
    	for1(i, 1, n) add(s, i+n, 1, getint());
    	for1(i, 1, n) add(s, i, 1, 0);
    	for1(i, 1, n) add(i+n, t, 1, 0);
    	for1(i, 1, m) {
    		int x=getint(), y=getint();
    		if(x>y) swap(x, y);
    		add(x, y+n, 1, getint());
    	}
    	printf("%d
    ", mcf(s, t));
    	return 0;
    }
    


    好神的题!!!!!!!!!!!!!!!!!!!

    建图:

    • 源向i+n连容量1,费用为能力爆发的费用
    • 源向i连容量1,费用为0
    • i+n向汇连容量1,费用0
    • 如果有边x<y,连x到y+n容量为1,费用为时间

    然后跑最小费用最大流

    为什么这样就行了呢?

    由于每一个点都访问到,因此只需要连边即可,一个是到了i点,我们从这个点选择下一个点。一个是到过i点(即上面的i+n),用来标记这个点是否已经到过!

    所以我们只需要考虑每个已到过的点是如何到的即可。

    1. 顺移到的,这样不需要考虑是从哪个转移过来的。

    2. 连边过来了,由于每一个点肯定访问过了,所以无论哪个点都能做自己的前驱。

    所以按照上面的连边就行了。

     
  • 相关阅读:
    Microsoft Enterprise Library 5.0 系列(二) Cryptography Application Block (初级)
    Microsoft Enterprise Library 5.0 系列(五) Data Access Application Block
    Microsoft Enterprise Library 5.0 系列(八) Unity Dependency Injection and Interception
    Microsoft Enterprise Library 5.0 系列(九) Policy Injection Application Block
    Microsoft Enterprise Library 5.0 系列(三) Validation Application Block (高级)
    软件研发打油诗祝大家节日快乐
    从挖井的故事中想到开发管理中最容易忽视的几个简单道理
    ITIL管理思想的执行工具发布
    管理类软件设计“渔”之演化
    20070926日下午工作流与ITILQQ群 事件管理 讨论聊天记录
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4152240.html
Copyright © 2011-2022 走看看