1. 程式人生 > >[HNOI2009]最小圈

[HNOI2009]最小圈

.com acs || 分答 code www. amp memset class

傳送門

題意:在帶權有向圖中,求圖中所有環的平均值的最小值(環的平均值即構成環的所有邊的邊權的平均值).

分析:理解題意,我們要求的實際上就是\(\frac{\sum_{i=1}^k{c_i}}{k}\)

\(x=\frac{\sum_{i=1}^k{c_i}}{k}\)

整理得\(\sum_{i=1}^k{c_i}-k*x=0\)

繼續得\(\sum_{i=1}^k(c_i-x)=0\)

\(f(x)=\sum_{i=1}^k(c_i-x)\)

f(x)隨x增大而減小,即f(x)是一個遞減函數,既然具有單調性,就要想到二分答案.

我們直接二分要求的答案x,check時把每條邊的邊權看作\(c_i-x\)
,如果此次二分的值合法,即\(\frac{\sum_{i=1}^k{c_i}}{k}<x\),根據上面的推導得\(\sum_{i=1}^k(c_i-x)<0\),即圖中存在一個負環.

所以本題就轉換為了二分x,把每條邊權看作\(c_i-x\),然後判斷圖中是否存在負環.

我寫的是dfs+spfa判負環(如果不會判負環,請看廣告).

註意本題是實數域上的二分答案.

int n,m,visit[3005];
int tot,head[3005],nxt[10005],to[10005];
double eps=1e-10;
//要求保留8位小數,二分精度可以設置為1e-(8+2)
double w[10005],dis[3005];
void add(int a,int b,double c){
    nxt[++tot]=head[a];
    head[a]=tot;
    to[tot]=b;
    w[tot]=c;
}
bool dfs_spfa(int x,double mid){
    visit[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
//記得邊權看作w[i]-mid 
        if(dis[y]>dis[x]+w[i]-mid){
            dis[y]=dis[x]+w[i]-mid;
            if(visit[y]||dfs_spfa(y,mid))
                return 1;
        }
    }
    visit[x]=0;
    return 0;
}
bool check(double mid){
    memset(visit,0,sizeof(visit));
    for(int i=1;i<=n;i++)dis[i]=0;
//這裏為什麽dis數組全部初始化為零我也不懂
//反正我試了賦值為無窮大是錯的
//(我不會說出來我無腦試了好幾個初值)
    for(int i=1;i<=n;i++)
        if(dfs_spfa(i,mid))return 1;
    return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int a,b;double c;
        scanf("%d%d%lf",&a,&b,&c);
        add(a,b,c);
    }
    double l=-1e9,r=1e9,mid;
//註意一下二分邊界,因為環的最小平均值可能為負數
    while(l+eps<r){//實數二分答案模板
        mid=(l+r)/2.0;
        if(check(mid))r=mid;
        else l=mid;
    }
    printf("%.8lf\n",r);
    return 0;
}

[HNOI2009]最小圈