01分数规划,SPFA——POJ3621

题目链接

题目含义

有一个有向图,每个点都有点权,每条边也都有边权

然后有头牛要从任一点出发,经过至少两个点后回到原点,即走一个环

问经过的sigma点权/sigma边权最大是多少

题目分析

最开始没想出来,看了网上说用SPFA判负环也有点不明白

因为不是说每个点的点权只得到一次吗,SPFA是怎么处理这点的?

然后想到,这是因为我在考虑排除原路返回的情况

因为原路返回没得到点权,反而增加了边权肯定是不划算的

但是SPFA判的负环根本不会原路返回啊!!!如果原路返回了,那还是环吗,那就是重叠边了呀

所以是我多虑了

——————————————————————

若sigmaPi/sigmaWi取得最大,那么任意一个答案ans必定<=sigmaPi/sigmaWi

转换一下就是 sigmaPi-sigmaWi*ans>=0  =>   sigma(  Pi-ans*Wi )>=0

然后就是二分,如果ans满足这个式子,low=mid,否则high=mid

但是这个sigma(Pi-ans*Wi)怎么求呢

只能想到用SPFA,如果有负环就说明从某一起点走一圈回到这个起点时sigma边权<0

所以将边权换成我们的Pi-ans*Wi就可以做了

题目代码

初始化手写就能过,memset就会wa,不知道为什么......

玄学memset

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
const int maxm=5007;
const int maxn=1007;
const double eps=1e-7;
struct edge{
    int to,next,dis;
}e[maxm];
int head[maxm],tot;
void add(int u,int v,int w){
    e[tot].dis=w;
    e[tot].to=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
int f[maxn],n,m,a,b,c;
bool SPFA(double x){
    double dis[maxn];
    bool vis[maxn];
    int num[maxn];
    for(int i=1;i<=n;i++){
        dis[i]=1e100;
        vis[i]=false;
        num[i]=0;
    }
//    memset(dis,1e100,sizeof(dis));
//    memset(vis,false,sizeof(vis));
//    memset(num,0,sizeof(num));
    queue<int>q;
    q.push(1);
    dis[1]=0;
    vis[1]=true;
    num[1]++;
    while(!q.empty()){
        int u=q.front();q.pop();
        vis[u]=false;
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(dis[v]>dis[u]+x*e[i].dis-f[v]){
                dis[v]=dis[u]+x*e[i].dis-f[v];
                if(!vis[v]){
                    vis[v]=true;
                    q.push(v);
                    num[v]++;
                    if(num[v]>n)return true;
                }
            }
        }
    }
    return false;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&f[i]);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    double l=0.0,r=10000,mid;
    while(r-l>eps){
        mid=(l+r)/2;
        if(SPFA(mid))l=mid;
        else r=mid;
    }
    printf("%.2f\n",mid);
    return 0;
}

 

posted @ 2019-08-10 19:26  helman78  阅读(124)  评论(0编辑  收藏  举报