【bzoj1486】 HNOI2009—最小圈

http://www.lydsy.com/JudgeOnline/problem.php?id=1486 (题目链接)

题意:给出一张有向图,规定一个数值u表示图中一个环的权值/环中节点个数。求最小的u。

Solution 
  尼玛今天考试题,不知道是考二分的话这真的做不出。。 
  二分一个答案ans,这个答案可行当且仅当ans>=∑w/cnt,cnt表示环中节点个数。移项,ans*cnt-∑w>=0,而w的个数又正好等于cnt,所以最后的式子变成了: ∑i=0n(ans−w)>=0

  这个式子看着很和谐对吧。没错,只要将边的权值全部减去ans后,spfa判断图中是否存在负权环即可。

代码:

// bzoj1486
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 2147483640
#define eps 0.000000001
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
inline LL getint() {
    int f,x=0;char ch=getchar();
    while (ch<=‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1;else f=1;ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

const int maxn=3010,maxm=10010;
struct edge {int to,next;double w;}e[maxm<<1];
double dis[maxn];
int vis[maxn],head[maxn],cnts[maxn],cnt,n,m;

void link(int u,int v,double w) {
    e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w;
}
bool SPFA() {
    queue<int> q;
    for (int i=1;i<=n;i++) {
        dis[i]=0;
        q.push(i);
        vis[i]=1;
        cnts[i]=0;
    }
    dis[1]=0;
    while (q.size()) {
        int x=q.front();
        vis[x]=0;q.pop();
        for (int i=head[x];i;i=e[i].next)
            if (dis[e[i].to]>dis[x]+e[i].w) {
                dis[e[i].to]=dis[x]+e[i].w;
                if (!vis[e[i].to]) {
                    if (++cnts[e[i].to]>30) return 0;
                    q.push(e[i].to);
                    vis[e[i].to]=1;
                }
            }
    }
    return 1;
}
bool ck(double mid) {
    bool flag=1;
    for (int i=1;i<=cnt;i++) e[i].w-=mid;
    if (SPFA()) flag=0;
    for (int i=1;i<=cnt;i++) e[i].w+=mid;
    return flag;
}
int main() {
    scanf("%d%d",&n,&m);
    double L=inf,R=0,ans;
    for (int u,v,i=1;i<=m;i++) {
        double w;
        scanf("%d%d%lf",&u,&v,&w);
        link(u,v,w);
        R=max(R,w);L=min(L,w);
    }
    while (L+eps<R) {
        double mid=(L+R)/2;
        if (ck(mid)) R=mid,ans=mid;
        else L=mid;
    }
    printf("%.8lf",ans);
    return 0;
}

  

时间: 2024-08-03 15:34:55

【bzoj1486】 HNOI2009—最小圈的相关文章

BZOJ1486 [HNOI2009]最小圈

今年的最后一篇了呢...好伤感的说,2014年还有1h就过去了 不不不回到正题,这道题嘛~看上去好神啊! 看到此题,我们可以联想到最优比例MST,于是就有了方法: 首先二分答案ans,判断ans是否可行,那如何判断呢? 每条边边权 - ans,之后在新的图中找负环即可.(可以用dfs版的spfa) 1 /************************************************************** 2 Problem: 1486 3 User: rausen 4 L

1486: [HNOI2009]最小圈 - BZOJ

在机房的小伙伴提醒是二分之后,我想到了是判负环,所以我用spfa,而且我保持dis都是小于等于0,本以为这样就能过了,可是还是有一个点达到了3.8s左右(其他都是0.0几秒) 所以还是写了dfs版本,还是一样每次都保持dis小于等于0,当发现有一个点在栈中,你又可以更新他的dis,那么就有负环了 1 const 2 maxn=3010; 3 maxm=10010; 4 inf=99999; 5 var 6 first:array[0..maxn]of longint; 7 next,last:a

bzoj 1486: [HNOI2009]最小圈 dfs求负环

1486: [HNOI2009]最小圈 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1022  Solved: 487[Submit][Status] Description 最开始写floyd求负环结果TLE了,改成dfs后速度变成原来的100+倍.反正还是比较神奇.

[HNOI2009]最小圈

hnoi2009最小圈 Description #include<iostream> #include<cstdio> #include<cstring> #define maxn 3005 #define maxe 10005 using namespace std; struct Edge { int v; double w; int next; }e[maxe]; int head[maxn],cnts=0,n,m; double eps=1e-9,dis[max

BZOJ 1486: [HNOI2009]最小圈( 二分答案 + dfs判负圈 )

二分答案m, 然后全部边权减掉m, 假如存在负圈, 那么说明有平均值更小的圈存在. 负圈用dfs判断. --------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x)) #define

bzoj千题计划227:bzo1486: [HNOI2009]最小圈j

http://www.lydsy.com/JudgeOnline/problem.php?id=1486 二分答案 dfs版spfa判负环 #include<queue> #include<cstdio> #include<cstring> #include<iostream> #define N 3001 #define M 10001 using namespace std; int n; int tot,front[N],nxt[M],to[M]; d

【bzoj1486】[HNOI2009]最小圈 分数规划+Spfa

题目描述 样例输入 4 5 1 2 5 2 3 5 3 1 5 2 4 3 4 1 3 样例输出 3.66666667 题解 分数规划+Spfa判负环 二分答案mid,并将所有边权减去mid,然后再判负环,若有负环则调整下界,否则调整上界,直至上下界基本重合. 证明:显然 由于有(c+d)/(a+b+k)>(c+d)/(a+b)≥min(c/a,d/b),所以两个相交环形成的新环一定不是最优解,即答案一定是简单环. 如果存在环使得边权和/点数<mid,那么就有边权和<点数*mid. 又因

【dfs判负环】BZOJ1489: [HNOI2009]最小圈

Description 找出一个平均边权最小的圈. Solution 经典问题,二分答案判断有无负环. 但数据范围大,普通spfa会超时,于是用dfs判负环(快多了). 思路是dis设为0,枚举每个点u,如果d(u)+w<d(v)就搜v,如果搜到的节点曾搜到过说明找到了负环. 感慨一下dfs真是神奇. Code 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespac

bzoj 1486: [HNOI2009]最小圈

二分答案再判负环 #include<cstdio> #include<algorithm> using namespace std; int read_p,read_ca; inline int read(){ read_p=0;read_ca=getchar(); while(read_ca<'0'||read_ca>'9') read_ca=getchar(); while(read_ca>='0'&&read_ca<='9') read