poj 3621 二分+spfa判负环

来源:互联网 发布:java socket断点下载 编辑:程序博客网 时间:2024/05/01 12:34

http://poj.org/problem?id=3621

求一个环的{点权和}除以{边权和},使得那个环在所有环中{点权和}除以{边权和}最大。

0/1整数划分问题

令在一个环里,点权为v[i],对应的边权为e[i], 
即要求:∑(i=1,n)v[i]/∑(i=1,n)e[i]最大的环(n为环的点数), 
设题目答案为ans, 
即对于所有的环都有 ∑(i=1,n)(v[i])/∑(i=1,n)(e[i])<=ans 
变形得ans* ∑(i=1,n)(e[i])>=∑(i=1,n)(v[i]) 
再得 ∑(i=1,n)(ans*e[i]-v[i]) >=0 
稍分析一下,就有: 
当k<ans时,就存在至少一个环∑(i=1,n)(k*e[i]-v[i])<0,即有负权回路(边权为k*e[i]-v[i]); 
当k>=ans时,就对于所有的环∑(i=1,n)(k*e[i]-v[i])>=0,即没有负权回路。 
然后我们就可以使新的边权为k*e[i]-v[i],用spfa来判断负权回路,二分ans。

#include <iostream>#include <cstdlib>#include <cstdio>#include <queue>#include <cstring>#include <algorithm>#define RD(x) scanf("%d",&x)#define RD2(x,y) scanf("%d%d",&x,&y)#define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)#define clr0(x) memset(x,0,sizeof(x))#define clr1(x) memset(x,-1,sizeof(x))using namespace std;typedef long long LL;const int maxn = 1005,maxm = 105;const double eps = 1e-3;const int inf = 0x7fffffff;int v[maxn];struct edge{    int v,w,next;    edge(){};    edge(int vv,int ww,int nnext):v(vv),w(ww),next(nnext){};}e[maxn*5];int head[maxn],vis[maxn],_v[maxn],cnt[maxn],ecnt,n,m;double dist[maxn];void add(int u,int v,int w){    e[ecnt] = edge(v,w,head[u]);    head[u] = ecnt++;}void init(){    for(int i = 1;i <= n;++i)        RD(_v[i]);    ecnt = 0;    clr1(head);//判负环的初始化    int u,v,w;    while(m--){        RD3(u,v,w);        add(u,v,w);    }    return ;}bool spfa(double mid){    clr0(vis),clr0(cnt);    fill(dist,dist + n + 1,inf);    dist[1] = 0;    queue<int> q;    q.push(1);    while(!q.empty()){        int u = q.front();        q.pop();        vis[u] = false;        cnt[u]++;        if(cnt[u] > n)            return true;        for(int i = head[u];i != -1;i = e[i].next){            int v = e[i].v;            double tmp = mid*e[i].w - _v[v];//"边权"            if(dist[u] + tmp < dist[v]){                dist[v] = dist[u] + tmp;                if(!vis[v]){                    vis[v] = true;                    q.push(v);                }            }        }    }    return false;}void work(){    double l = 0,r = 10000,mid,ans;    while(r - l > eps){        mid = (l + r)/2;        if(spfa(mid)){            ans = mid;            l = mid - 0.000001;        }else            r = mid + 0.000001;    }    printf("%.2f\n",ans);}int main(){    while(~RD2(n,m)){        init();        work();    }    return 0;}


0 0
原创粉丝点击