AcWing 361. 觀光奶牛(01分數規劃)(判正環)

題幹:

給定一張L個點、P條邊的有向圖,每個點都有一個權值f[i],每條邊都有一個權值t[i]。
求圖中的一個環,使“環上各點的權值之和”除以“環上各邊的權值之和”最大。
輸出這個最大值。
注意:數據保證至少存在一個環。
輸入格式
第一行包含兩個整數L和P。
接下來L行每行一個整數,表示f[i]。
再接下來P行,每行三個整數a,b,t[i],表示點a和b之間存在一條邊,邊的權值爲t[i]。
輸出格式
輸出一個數表示結果,保留兩位小數。
數據範圍
2≤L≤1000,
2≤P≤5000,
1≤f[i],t[i]≤1000

輸入樣例:
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
輸出樣例:
6.00

思路:

根據題意,我們要求的首先是一個正環,然後需要讓這個環上的點權和除以邊權和最大。fi/ei\sum_{}f_i / \sum_{}e_i,fi表示點權,ei表示邊權。
既然是有向圖而已要有環,我們可以把邊權放在出點上,這樣一個點即表示了邊權也表示了點權,會簡單一些。。。

這就是一個比較標準的01分數規劃模型即i=1nfixii=1neixi\frac{\sum_{i=1}^{n}f_i*x_i}{\sum_{i=1}^{n}e_i*x_i}其中xi=0或xi=1。
我們設一組{x1,x2...xnx_1,x_2...x_n}滿足i=1n(fimidei)>=0\sum_{i=1}^{n}(f_i-mid*e_i)>=0
①若存在,則:i=1nfixii=1neixi>=mid\frac{\sum_{i=1}^{n}f_i*x_i}{\sum_{i=1}^{n}e_i*x_i}>=mid
即mid比要求的最大值小
②若不存在,則:i=1nfixii=1neixi<mid\frac{\sum_{i=1}^{n}f_i*x_i}{\sum_{i=1}^{n}e_i*x_i}<mid
即mid比要求的最大值大
這就形成了一個二分判斷,即01分數規劃問題可以用二分來做。

然後問題就轉換成了找到一個正環,二分查找fi/eimid\sum_{}f_i / \sum_{}e_i ?mid
然後思考二分的左右邊界,因爲是有向圖,所以左邊界是i=110001/i=1500010000\sum_{i=1}^{1000}1 / \sum_{i=1}^{5000}1000 \approx 0,右邊界是i=110001000/i=110001=1000\sum_{i=1}^{1000}1000 / \sum_{i=1}^{1000}1 =1000
然後注意下因爲要輸出分數,所以mid和dis[]要用double。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1010,M=5010;
int n,m;
int f[N],q[N],cnt[N];
double dis[N];
int idx,h[N],ne[M],e[M],wt[M];
bool vis[N];
void add(int a,int b,int c){
    e[idx]=b;
    wt[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
bool check(double mid){
    memset(dis,0,sizeof(dis));
    memset(cnt,0,sizeof(cnt));
    memset(vis,0,sizeof(vis));
    int hh=0,tt=0;
    for(int i=1;i<=n;i++){
        q[tt++]=i;
        vis[i]=true;
    }
    while(tt!=hh){
        int t=q[hh++];
        if(hh==N)   hh=0;
        vis[t]=false;
        for(int i=h[t];~i;i=ne[i]){
            int j=e[i];
            if(dis[j]<dis[t]+f[t]-mid*wt[i]){
                dis[j]=dis[t]+f[t]-mid*wt[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;
                if(!vis[j]){
                    q[tt++]=j;
                    if(tt==N) tt=0;
                    vis[j]=true;
                }
            }
        }
    }
    return false;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&f[i]);
    memset(h,-1,sizeof(h));
    while(m--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    double l=0,r=1e6;
    while(r-l>1e-5){
        double mid=(r+l)/2;
        if(check(mid)) l=mid;
        else    r=mid;
    }
    printf("%.2lf\n",l);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章