POJ3621 Sightseeing Cows 和 CH6B12 最优高铁环 和 SCOI2014 方伯伯运椰子

Sightseeing Cows

给定一个n(2 <= n <= 1000)个点,m(2 <= m <= 5000)条边的有向图,给定每个点的点值f(i)和每条边的权值w(i),求一个环使得路径上点权和除以边权和最大。

简单来说就是最优比率生成环。

题解

01分数规划问题,按套路二分答案。

现在问题转化为了判断可行。把边权变成\(mid*T-F\),这样可行的话一个环的和<0,问题转化成了判断负环。

而如何判断负环呢?可以记录入队次数,也可以记录最短路边数。而后一种效率更高。

时间复杂度\(O(nm\log v)\)

co int N=1e3+1,M=5e3+1;
co double eps=1e-6;
int n,m,c[N],f[N],x[M],y[M],z[M];
int Head[N],Edge[M],Next[M],tot;
double Leng[M],d[N];
bool v[N];

il void add(int x,int y,double z){
	Edge[++tot]=y,Leng[tot]=z,Next[tot]=Head[x],Head[x]=tot;
}
bool judge(double w){
	tot=0,memset(Head,0,sizeof Head);
	for(int i=1;i<=m;++i) add(x[i],y[i],w*z[i]-f[x[i]]);
	queue<int> q;
	for(int i=1;i<=n;++i) q.push(i),d[i]=0,v[i]=1;
	memset(c,0,sizeof c);
	while(q.size()){
		int x=q.front();q.pop();
		v[x]=0;
		for(int i=Head[x];i;i=Next[i]){
			int y=Edge[i];
			if(d[y]>d[x]+Leng[i]){
				d[y]=d[x]+Leng[i];
				if((c[y]=c[x]+1)>=n) return 1;
				if(!v[y]) q.push(y),v[y]=1;
			}
		}
	}
	return 0;
}
int main(){
	read(n),read(m);
	for(int i=1;i<=n;++i) read(f[i]);
	for(int i=1;i<=m;++i) read(x[i]),read(y[i]),read(z[i]);
	double l=0,r=1000;
	while(r-l>eps){
		double mid=(l+r)/2;
		if(judge(mid)) l=mid;
		else r=mid;
	}
	printf("%.2f",l);
	return 0;
}

背景

幻影国建成了当今世界上最先进的高铁,该国高铁分为以下几类:
S---高速光子动力列车---时速1000km/h
G---高速动车---时速500km/h
D---动车组---时速300km/h
T---特快---时速200km/h
K---快速---时速150km/h
该国列车车次标号由上述字母开头,后面跟着一个正整数(<=1000)构成。

描述

由于该国地形起伏不平,各地铁路的适宜运行速度不同。因此该国的每一条行车路线都由K列车次构成。例如:K=5的一条路线为:T120-D135-S1-G12-K856。当某一条路线的末尾车次与另一条路线的开头车次相同时,这两条路线可以连接起来变为一条更长的行车路线。显然若干条路线连接起来有可能构成一个环。

若有3条行车路线分别为:
x1-x2-x3
x3-x4
x4-x5-x1
x1~x5车次的速度分别为v1~v5
定义高铁环的值为(环上各条行车路线速度和)的平均值,即:
[(v1+v2+v3)+(v3+v4)+(v4+v5+v1)]/3.
所有高铁环的值的最大值称为最优高铁环的值。
给出M条行车路线,求最优高铁环的值。

输入格式

第一行为行车路线条数M
接下来M行每行一条行车路线,由若干车次构成,各车次之间用'-'号隔开,车次的标号方式如上所述。
数据保证输入的合法性。

输出格式

最优高铁环的值。四舍五入到最接近的整数。
若不存在这样的环,输出-1.

样例输入

3
T120-D135-S1
S1-G12
G12-K856-T120

样例输出

1283

数据范围与约定

  • 50%的数据:0<M<=1000
    100%的数据,0<M<=50000
    每条行车路线车次个数不超过20,数据保证结果不超过2^31-1。

样例解释

[(200+300+1000)+(1000+500)+(500+150+200)]/3=1283

题解

把每段行车路线的起点终点车次看成节点,则给出的就是一些边,然后要求一个01分数规划问题。

二分答案后,找负(正)环即可。

co int N=5e4+1;
co double eps=1e-3;
int n,m,st,ed,t;
int head[N],edge[N],next[N],tot;
double d[N],s[N],leng[N];
bool v[N];

il void add(int x,int y){
	edge[++tot]=y,next[tot]=head[x],head[x]=tot;
}
il int f(char ch){
	switch(ch){
		case 'S': t=0;return 1000;
		case 'G': t=1;return 500;
		case 'D': t=2;return 300;
		case 'T': t=3;return 200;
		case 'K': t=4;return 150;
	}
	return -1;
}
bool dfs(int x){
	v[x]=1;
	for(int i=head[x];i;i=next[i]){
		int y=edge[i];double z=leng[i];
		if(d[y]>d[x]+z){
			d[y]=d[x]+z;
			if(v[y]||dfs(y)) return 1;
		}
	}
	v[x]=0;
	return 0;
}
bool pd(double x){
	for(int i=1;i<=m;++i) leng[i]=x-s[i];
	memset(v,0,sizeof v);
	memset(d,0x3f,sizeof d);
	for(int i=1;i<N;++i)
		if(dfs(i)) return 1;
	return 0;
}
int main(){
	read(m);
	for(int i=1;i<=m;++i){
		static char str[150];
		scanf("%s",str),n=strlen(str);
		int x=0;
		bool flag=0;
		for(int j=0;j<n;++j){
			if(isdigit(str[j])) x=x*10+str[j]-'0';
			else if(str[j]=='-'){
				if(!flag) st=x+t*10000;
				flag=1;
				x=0;
			}
			else s[i]+=f(str[j]);
		}
		ed=x+t*10000;
		add(st,ed);
	}
	double l=0,r=0x7fffffff,ans=-1; // edit 1: -1
	while(r-l>eps){
		double mid=(l+r)/2;
		if(pd(mid)) l=ans=mid;
		else r=mid;
	}
	printf("%.0lf\n",ans);
	return 0;
}

方伯伯运椰子

四川的方伯伯为了致富,决定引进海南的椰子树。方伯伯的椰子园十分现代化,椰子园中有一套独特的交通系统。

现在用点来表示交通节点,边来表示道路。这样,方伯伯的椰子园就可以看作一个有 \(n + 2\) 个交通节点,\(m\) 条边的有向无环图。\(n +1\) 号点为入口,\(n +2\) 号点为出口。每条道路都有六个参数,\(u_i, v_i, a_i, b_i, c_i, d_i\),分别表示,该道路从 \(u_i\) 号点通向 \(v_i\) 号点,将它的容量压缩一次要 \(a_i\) 的花费,容量扩大一次要 \(b_i\) 的花费,该条道路当前的运输容量上限为 \(c_i\),并且每单位运输量通过该道路要 \(d_i\) 的费用。

在这个交通网络中,只有一条道路与起点相连。因为弄坏了这条道路就会导致整个交通网络瘫痪,聪明的方伯伯决定绝不对这条道路进行调整,也就是说,现在除了这条道路之外,对其余道路都可以进行调整。

有两种调整方式:

  1. 选择一条道路,将其进行一次压缩,这条道路的容量会下降 1 单位。
  2. 选择一条道路,将其进行一次扩容,这条道路的容量会上升 1 单位。

一条道路可以被多次调整。

由于很久以前,方伯伯就请过一个工程师,对这个交通网络进行过一次大的优化调整。所以现在所有的道路都被完全的利用起来了,即每条道路的负荷都是满的(每条道路的流量等于其容量)。但方伯伯一想到自己的海南椰子会大丰收,就十分担心巨大的运输量下,会导致过多的花费。因此,方伯伯决定至少进行一次调整,调整之后,必须要保持每条道路满负荷,且总交通量不会减少。

设调整后的总费用是 \(Y\),调整之前的总费用是 \(X\)。现在方伯伯想知道,最优调整比率是多少,即假设他进行了 \(k\) 次调整,\((X - Y)/k\) 最大能是多少?

注:总费用 = 交通网络的运输花费 + 调整的花费

对于所有数据,\(1 \leq N \leq 5000,\ 0 \leq M \leq 3000,\ 1 \leq U_i,V_i \leq N+2,\ 0 \leq A_i,B_i \leq 500,\ 0 \leq C_i \leq 10000,\ 0 \leq D_i \leq 1000\)

题解

一个可以增容和减容的网络流图?

由于要满流,所以减容操作的花费是\(a_i-d_i\),增容操作的花费是\(b_i+d_i\)

01分数规划,二分答案\(\text{mid}\)

起点出边不能变,所以总流量不会变。那么增加的流量和减少的流量抵消了。

有向无环图,所以改的应该是个“环”?\(Y-X+k\cdot \text{mid}\leq 0\),所以找的应该是负环。

负环要把所有点都放进去……

CO int N=5e3+10;
CO float128 inf=1e18;
struct edge {int y,w;};
vector<edge> to[N];
float128 dis[N];
int vis[N],cnt[N];

int main(){
	int n=read<int>();
	for(int m=read<int>();m--;){
		int x=read<int>(),y=read<int>();
		int a=read<int>(),b=read<int>(),c=read<int>(),d=read<int>();
		to[x].push_back({y,b+d});
		if(c) to[y].push_back({x,a-d});
	}
	float128 L=0,R=1e9;
	for(int t=1;t<=40;++t){
		float128 mid=(L+R)/2;
		function<bool()> check=[&]()->bool{
			deque<int> que;
			for(int i=1;i<=n+2;++i){
				dis[i]=0,cnt[i]=0;
				que.push_back(i),vis[i]=1;
			}
			while(que.size()){
				int x=que.front();
				que.pop_front(),vis[x]=0;
				for(CO edge&e:to[x])if(dis[e.y]>dis[x]+e.w+mid){
					dis[e.y]=dis[x]+e.w+mid;
					if((cnt[e.y]=cnt[x]+1)>=n) return 1;
					if(!vis[e.y]) que.push_back(e.y),vis[e.y]=1;
				}
			}
			return 0;
		};
		check()?L=mid:R=mid;
	}
	printf("%.2Lf\n",L);
	return 0;
}

posted on 2020-06-10 09:22  autoint  阅读(354)  评论(0编辑  收藏  举报

导航