【2018/10/11測試T3】【WOJ 4075】葫蘆
【題目】
題目描述:
Tom 最喜歡的歌曲就是《葫蘆娃》。
一日表演唱歌,他盡了洪荒之力,唱響心中聖歌。
隨之,Tom 進入了葫蘆世界。
葫蘆世界有 個葫蘆,標號為 ~ 。 個葫蘆由 條藤連線,每條藤連線了兩個葫蘆, 這些藤構成了一張有向無環圖。Tom 爬過每條藤都會消耗一定的能量。
Tom 站在 號葫蘆上(你可以認為葫蘆非常大,可以承受 Tom 的體重),他想沿著藤爬到 號葫蘆上,其中每個葫蘆只經過一次。
Tom 找到一條路徑,使得消耗的能量與經過的葫蘆數的比值最小。
輸入格式:
輸入檔案第一行兩個正整數 ,分別表示葫蘆的個數和藤數。
接下來 行,每行三個正整數 ,描述一條藤,表示這條藤由 連向 ,Tom 爬過這條藤需要消耗 點能量。
輸出格式:
一行一個實數,表示答案(誤差不超過 )。
樣例資料:
輸入 4 6 1 2 1 2 4 6 1 3 2 3 4 4 2 3 3 1 4 8
輸出 2.000
提示:
有 4 種爬法: ,消耗能量 ,經過 個葫蘆,比值為 。 ,消耗能量 ,經過 個葫蘆,比值為 。 ,消耗能量 ,經過 個葫蘆,比值為 。 ,消耗能量 ,經過 個葫蘆,比值為 。
所以選第三種或第四種方案,答案為 。
測試點編號 | 特殊說明 | ||
---|---|---|---|
除 外,所有葫蘆的入度均為 | |||
所有從 到 的路徑經過的葫蘆數相等 | |||
除 外,所有葫蘆的入度均為 | |||
所有從 到 的路徑經過的葫蘆數相等 | |||
對於所有資料,Tom 爬過每條藤消耗的能量不會超過 ,且一定存在一條從 到 的路徑。
【分析】
50 pts:
直接暴力 一遍即可
100 pts:
新建一個起點 ,連線一條 到 長度為 的邊,這樣做的好處是直接將問題轉化為長度和邊數(就不是點數了)的最小比值,更為方便
假設有一個數 ,對於任意一條由 條邊組成的路徑,若 合法,必然有: ; 移一下項,得: ; 再轉換一下,即 。
很顯然我們可以二分答案,每次將所有邊權減去 後求最短路,然後判斷 是否大於等於 ,若是,說明 ,需要把 調大,不然就說明 ,要把 調小
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;
}