1. 程式人生 > >【2018/10/11測試T3】【WOJ 4075】葫蘆

【2018/10/11測試T3】【WOJ 4075】葫蘆

【題目】

題目描述:

Tom 最喜歡的歌曲就是《葫蘆娃》。

一日表演唱歌,他盡了洪荒之力,唱響心中聖歌。

隨之,Tom 進入了葫蘆世界。

葫蘆世界有 nn 個葫蘆,標號為 11 ~ nnnn 個葫蘆由 mm 條藤連線,每條藤連線了兩個葫蘆, 這些藤構成了一張有向無環圖。Tom 爬過每條藤都會消耗一定的能量。

Tom 站在 11 號葫蘆上(你可以認為葫蘆非常大,可以承受 Tom 的體重),他想沿著藤爬到 nn 號葫蘆上,其中每個葫蘆只經過一次。

Tom 找到一條路徑,使得消耗的能量與經過的葫蘆數的比值最小。

輸入格式:

輸入檔案第一行兩個正整數 n,mn,m,分別表示葫蘆的個數和藤數。

接下來 mm 行,每行三個正整數 u,v,wu,v,w,描述一條藤,表示這條藤由 uu 連向 vv,Tom 爬過這條藤需要消耗 ww 點能量。

輸出格式:

一行一個實數,表示答案(誤差不超過 10310^{-3})。

樣例資料:

輸入 4 6 1 2 1 2 4 6 1 3 2 3 4 4 2 3 3 1 4 8

輸出 2.000

提示:

有 4 種爬法: 1  >41\;->4,消耗能量 88,經過 22 個葫蘆,比值為 8/2=48/2=41  >

2  >41\;->2\;->4,消耗能量 1+6=71+6=7,經過 33 個葫蘆,比值為 7/32.337/3≈2.331  >3  >41\;->3\;->4,消耗能量 2+4=62+4=6,經過 33 個葫蘆,比值為 6/3=26/3=21  >2  
>3  >41\;->2\;->3\;->4
,消耗能量 1+3+4=81+3+4=8,經過 44 個葫蘆,比值為 8/4=28/4=2

所以選第三種或第四種方案,答案為 22

測試點編號 nn mm 特殊說明
11 22 11
22 100100 9999 11 外,所有葫蘆的入度均為 11
33 100100 105105 所有從 11nn 的路徑經過的葫蘆數相等
44 100100 10001000
55 100100 10001000
66 199199 198198 11 外,所有葫蘆的入度均為 11
77 200200 231231 所有從 11nn 的路徑經過的葫蘆數相等
88 200200 20002000
99 200200 20002000
1010 200200 20002000

對於所有資料,Tom 爬過每條藤消耗的能量不會超過 10310^3,且一定存在一條從 11nn 的路徑。

【分析】

50 pts:

直接暴力 dfsdfs 一遍即可

100 pts:

新建一個起點 00,連線一條 0011 長度為 00 的邊,這樣做的好處是直接將問題轉化為長度和邊數(就不是點數了)的最小比值,更為方便

假設有一個數 ansans,對於任意一條由 kk 條邊組成的路徑,若 ansans 合法,必然有: (w1+w2+w3++wk)/k>=ans(w_1+w_2+w_3+…+w_k)/k>=ans; 移一下項,得: (w1+w2+w3++wk)>=ansk(w_1+w_2+w_3+…+w_k) >=ans*k; 再轉換一下,即 (w1ans)+(w2ans)+(w3ans)++(wkans)>=0(w_1-ans)+(w_2-ans)+(w_3-ans)+…+(w_k-ans)>=0

很顯然我們可以二分答案,每次將所有邊權減去 midmid 後求最短路,然後判斷 dnd_n 是否大於等於 00,若是,說明 ans>=midans>=mid,需要把 midmid 調大,不然就說明 ans<midans<mid,要把 midmid 調小

01分數規劃簡單題,但由於掌握不熟沒有拿滿分。。。

【程式碼】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 205
#define M 2005
#define eps 1e-8
using namespace std;
int n,m,t;
int first[N];
int v[M],w[M],next[M];
bool vis[N];
double ww[M],dis[N];
void add(int x,int y,int z)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
bool check(double mid)
{
	int x,y,i,j;
	for(i=1;i<=t;++i)  ww[i]=w[i]-mid;
	memset(dis,127,sizeof(dis));
	memset(vis,false,sizeof(vis));
	queue<int>q;dis[0]=0;q.push(0);
	while(!q.empty())
	{
		x=q.front();q.pop();
		vis[x]=false;
		for(i=first[x];i;i=next[i])
		{
			y=v[i];
			if(dis[y]>dis[x]+ww[i])
			{
				dis[y]=dis[x]+ww[i];
				if(!vis[y])
				{
					vis[y]=true;
					q.push(y);
				}
			}
		}
	}
	return dis[n]>=0;
}
int main()
{
//	freopen("calabash.in","r",stdin);
//	freopen("calabash.out","w",stdout);
	int x,y,z,i;
	scanf("%d%d",&n,&m);
	add(0,1,0);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	double l=0,r=1e6,mid;
	while(r-l>eps)
	{
		mid=(l+r)/2;
		if(check(mid))  l=mid;
		else  r=mid;
	}
	printf("%.3lf",l);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}