【题解】 [HNOI2009] 最小圈 (01分数规划,二分答案,负环)

题目背景

如果你能提供题面或者题意简述,请直接在讨论区发帖,感谢你的贡献。

题目描述

对于一张有向图,要你求图中最小圈的平均值最小是多少,即若一个圈经过k个节点,那么一个圈的平均值为圈上k条边权的和除以k,现要求其中的最小值

输入输出格式

输入格式:

第一行2个正整数,分别为n和m

以下m行,每行3个数,表示边连接的信息,

输出格式:

一行一个数,表示最小圈的值,保留8位小数。

输入输出样例

输入样例#1: 复制
4 5
1 2 5
2 3 5
3 1 5
2 4 3
4 1 3
输出样例#1: 复制
3.66666667

说明

若设边权为vvv,那么n≤3000,m≤10000,v≤50000n\le 3000,m\le 10000,v\le 50000n3000,m10000,v50000

 

 

题解转自NaVi_Awson巨佬博客(快去访问 http://www.cnblogs.com/NaVi-Awson/p/7641518.html

题解

最小化平均值($01$分数规划)。

使用二分求解。对于一个猜测的$mid$,只需判断是否存在平均值小于$mid$的回路。

如何判断?

假设存在一个包含$k$条边的回路,回路上各边权值为$w_1$ ,$w_2$ ,$...$,$w_k$ ,那么平均值小于$midv$意味着:

$$w_1 +w_2 +...+w_k <k×mid$$

即:

$$(w_1 -mid)+(w_2 -mid)+...+(w_k -mid)<0$$

换句话说,只要把边$(a,b)$的权$w(a,b)$改成$w(a,b)-mid$,再判断新图中是否有负环即可。

存在负环,那么之前的不等式满足,即存在着更小的平均值,$r=mid$;不存在,$l=mid$。

 

不要脸的贴自己的代码:

 

//It is coded by Ning_Mew on 10.26
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const double CDN=0.000000001;
int n,m;
double L,R,mid;
double dist[10000+10];
int head[3000+10],t;
struct Edge{
    int next,to;
    double dis;
}edge[10000+10];
void add(int from,int to,double dis){
    edge[++t].next=head[from];
    edge[t].to=to;
    edge[t].dis=dis;
    head[from]=t;
}
bool vis[3000+10];
void clear(){
    memset(vis,false,sizeof(vis));
    memset(dist,0,sizeof(dist));
    return;
}
bool SPFA(int u){
    vis[u]=true;
    for(int i=head[u];i!=0;i=edge[i].next){
        int v=edge[i].to;
        if(dist[v]>dist[u]+edge[i].dis-mid){
            if(vis[v]){vis[u]=false;return true;}
            dist[v]=dist[u]+edge[i].dis-mid;
            if(SPFA(v)){vis[u]=false;return true;}
        }
    }
    vis[u]=false;return false;
}
bool check(){
    clear();
    for(int i=1;i<=n;i++){if(SPFA(i))return true;}
    return false;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        R=max(R,(double)z);
    }
    while(L+CDN<R){
        mid=(L+R)/2;
        if(check()){R=mid;}
        else L=mid;
    }
    printf("%0.8lf",(L+R)/2);
    return 0;
}

 

 

 

posted @ 2017-10-26 16:39  Ning_Mew  阅读(244)  评论(0编辑  收藏  举报