首页 > 代码库 > 【bzoj1486】 HNOI2009—最小圈

【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(answ)>=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;}

  

【bzoj1486】 HNOI2009—最小圈