M_sea

洛谷3199 [HNOI2009]最小圈
LuoguBZOJ分析这题感觉很Easy呀qwq如果你做题做得多就会知道,平均数的题一般可以减去某个值,然后变为判...
扫描右侧二维码阅读全文
11
2019/01

洛谷3199 [HNOI2009]最小圈

Luogu

BZOJ

分析

这题感觉很Easy呀qwq

如果你做题做得多就会知道,平均数的题一般可以减去某个值,然后变为判负数。

对于这个题,二分答案$mid$,并将每条边的权值减去$mid$,然后就变成了$\mathrm{SPFA}$判负环。

具体来说,如果存在负环,则应该R=mid,否则应该L=mid

然而这题用BFS的SPFA氵不过,用DFS的SPFA就能过了。

复杂度感觉有点假qwq

代码

//It is made by M_sea
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#define re register
using namespace std;

const int MAXN=3000+10;
const int MAXM=10000+10;
const double EPS=1e-9;

int n,m;

struct Edge { int v,nxt; double w; };
Edge e[MAXM];
int head[MAXN];

inline void addEdge(int u,int v,double w) {
    static int cnt=0;
    e[++cnt].v=v,e[cnt].w=w,e[cnt].nxt=head[u],head[u]=cnt;
}

double dis[MAXN];
int vis[MAXN];

inline int SPFA(int u,double mid) {
    vis[u]=1;
    for (re int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v; double w=e[i].w-mid;
        if (dis[u]+w<dis[v]) {
            dis[v]=dis[u]+w;
            if (vis[v]||SPFA(v,mid)) return 1;
        }
    }
    vis[u]=0; return 0;
}

inline int check(double mid) { //存在负环返回true
    for (re int i=1;i<=n;++i) dis[i]=0,vis[i]=0;
    for (re int i=1;i<=n;++i)
        if (SPFA(i,mid)) return 1;
    return 0;
}

int main() {
    scanf("%d%d",&n,&m);
    for (re int i=1;i<=m;++i) {
        int u,v; double w;
        scanf("%d%d%lf",&u,&v,&w);
        addEdge(u,v,w);
    }
    double L=-1e7,R=1e7;
    while (R-L>EPS) {
        double mid=(L+R)/2;
        if (check(mid)) R=mid;
        else L=mid;
    }
    printf("%.8f\n",L);
    return 0;
}
最后修改:2019 年 01 月 23 日 01 : 03 PM

发表评论