1. 程式人生 > 其它 >POJ 3621 Sightseeing Cows(最優比率環+spfa的dfs判環優化)

POJ 3621 Sightseeing Cows(最優比率環+spfa的dfs判環優化)

技術標籤:圖論dfs剪枝數學建模bellman–ford algorithm圖論

題目連結
題目大意:給你一個有向圖,每個點都有一個權值,每條邊都有一個長度,需要找到一個各點權值和/各邊長度和 最大的環出來,輸出這個比值。
分析:假設結點權值為 w i w_i wi,邊的長度為 l i l_i li設所求的比值 r = ∑ i = 1 m w v i ∗ x i ∑ i = 1 m l i ∗ x i ( x i = 0 或 1 , 1 < i < m ) r=\frac{\sum_{i=1}^{m}w_{vi}*x_i}{\sum_{i=1}^ml_i*x_i}(x_i=0或1,1<i<m)

r=i=1mlixii=1mwvixi(xi=01,1<i<m)我們要最大化這個 r r r假設這個最大值為ans
則滿足以下式子 a n s > = ∑ i = 1 m w v i ∗ x i ∑ i = 1 m l i ∗ x i ans>=\frac{\sum_{i=1}^{m}w_{vi}*x_i}{\sum_{i=1}^ml_i*x_i} ans>=i=1mlixii=1mwvixi
移項可得 ∑ i = 1 m w v i ∗ x i − a n s ∗ ∑ i = 1 m l i ∗ x i < = 0 \sum_{i=1}^{m}w_{vi}*x_i-ans*\sum_{i=1}^ml_i*x_i<=0
i=1mwvixiansi=1mlixi<=0

我們設 f ( a n s ) = ∑ i = 1 m w v i ∗ x i − a n s ∗ ∑ i = 1 m l i ∗ x i f(ans)=\sum_{i=1}^{m}w_{vi}*x_i-ans*\sum_{i=1}^ml_i*x_i f(ans)=i=1mwvixiansi=1mlixi
f ( a n s ) = ∑ i = 1 m d i ∗ x i , ( d i = w v i − a n s ∗ l i ) f(ans)=\sum_{i=1}^md_i*x_i,(d_i=w_{vi}-ans*l_i)
f(ans)=i=1mdixi,(di=wviansli)

這個關於ans的函式是隨ans變大而減小的,當f<=0時滿足條件,當且僅當f=0時ans取得最大值,則我們就可以把每條邊的權值設為 d i d_i di,然後二分ans,當用當前ans算出的f>0時則表明ans過小且不合法,當f<0時說明ans過大,而題目中要求的是一個環,當f<=0時是滿足題意的,但我們不好判斷,所以我們選擇去判斷f>0即圖中存在正環。
判正環的演算法可以用樸素的spfa也可dfs優化,最終的時間是樸素的是900+ms,而dfs優化則是30+ms
下面是dfs優化的

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#define MAIN main
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int N=1e3+10,M=5e3+10;
struct edge
{
    int v,next,w;
    double d;
}e[M<<1];
int head[N],cnt;
void add(int u,int v,int w)
{
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int w[N],n,m,vis[N],ok;
double dis[N];
void dfs(int u)
{
    if(ok) return;
    vis[u]=1;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(dis[v]<dis[u]+e[i].d)
        {
            dis[v]=dis[u]+e[i].d;
            if(vis[v])
            {
                ok=1;
                return;
            }
            dfs(v);
        }
    }
    vis[u]=0;
}
bool judge(double mid)
{
    for(int i=1;i<=cnt;i++) e[i].d=w[e[i].v]*1.0-mid*(e[i].w*1.0);
    for(int i=1;i<=n;i++) vis[i]=0,dis[i]=0;
    ok=0;
    for(int i=1;i<=n;i++){
        if(!vis[i]) dfs(i);
        if(ok) return true;
    }
    return false;
}
int MAIN()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=m;i++){
        int u,v,l;
        scanf("%d%d%d",&u,&v,&l);
        add(u,v,l);
    }
    double l=0,r=1e6,mid,ans;
    while(r-l>0.0001)
    {
        mid=(l+r)/2;
        if(judge(mid)) l=mid;
        else ans=mid,r=mid;
    }
    printf("%.2f\n",ans);
    return 0;
}