POJ 3621 Sightseeing Cows 最優比例環
阿新 • • 發佈:2018-12-09
題意:
給你L個點,P條邊,每個點和邊都有各自的權值,現在要你求一個環,使得這個環的點權/邊權最大。
做法:
自己做當然是。。。不會做啦。。這個坑已經放在這裡很久,之前做了最優比率生成樹的,這樣三個01分數規劃的專題就算完成了。因為我們要求一個最大的,那麼我們設這個比例為x,那麼我們會有一個公式,所以我們要二分一個最優值x,假設由這個公式出來的值是正的,那麼就說明還有一個更大的x值滿足要求,那麼就把l=mid繼續二分,否則就把r=mid。可是如果我們要判斷是否存在一個環滿足大於0有點困難,因為spfa可以判斷負環,那麼我們就可以把公式改一下,令,如果存在一個負環,那麼就是令l=mid;
這裡我在寫之前一直有個疑問,不是說所有的地點只有第一次走的時候有效嗎,那為什麼每條路里面都要減掉happy[i]呢,想了一會兒,然後發現,因為每個點只能在一個環中,所以每次如果鬆弛的時候更新到這個點的話,那麼就說明還有一個更符合要求的環需要這個點了,而如果我們要這個環,那麼這個點就只會被遍歷到一次,所以才要每一條邊都要減掉happy[i]。
#include<queue> #include<cstring> #include<cstdio> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; const int maxn=3005; const int inf=0x3f3f3f3f; const double eps=1e-7; int m,n,head[maxn],now,flag,vis[maxn],ct[maxn]; double dist[maxn],happy[maxn],w,maxhp,minhp,maxti,minti; struct node{ int to,next; double w; }e[maxn*120]; void add(int u,int v,double w){ e[now].to=v,e[now].next=head[u]; e[now].w=w; head[u]=now++; } int spfa(int x,double mid){ queue<int> q; dist[x]=0; vis[x]=1; ct[1]=1; q.push(x); while(!q.empty()){ int u=q.front(); q.pop(); if(++ct[u]>n) return 1; vis[u]=0; for(int i=head[u];~i;i=e[i].next){ int t=e[i].to; double w=mid*e[i].w-happy[t];; if(dist[t]-w>dist[u]){ dist[t]=w+dist[u]; if(!vis[t]){ vis[t]=1; q.push(t); } } } } return 0; } int ck(double mid){ memset(ct,0,sizeof(ct)); memset(vis,0,sizeof(vis)); memset(dist,125,sizeof(dist)); dist[1]=0; return spfa(1,mid); } int main(){ int x,y; memset(head,-1,sizeof(head)); maxhp=-1,maxti=-1,minhp=inf,minti=inf; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%lf",&happy[i]); maxhp=max(maxhp,happy[i]); minhp=min(minhp,happy[i]); } for(int i=0;i<m;i++){ scanf("%d%d%lf",&x,&y,&w); add(x,y,w); maxti=max(maxti,w); minti=min(minti,w); } double l=minhp/maxti,r=maxhp/minti,mid; while(r-l>eps){ mid=(l+r)/2; if(ck(mid)) l=mid; else r=mid; } printf("%.2f\n",mid); return 0; }