zoukankan      html  css  js  c++  java
  • 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
    ",mid);
        return 0;
    }
  • 相关阅读:
    自定义分页二
    CheckBox实现跨页面多选
    正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。
    DropDownList下拉框多选
    通用查询
    AutoCAD自动加载DLL文件的方法
    软件开发(团队管理)
    正确地做事与做正确的事
    C#.NET实现邮件的发送
    多附件的上传
  • 原文地址:https://www.cnblogs.com/helman/p/11332178.html
Copyright © 2011-2022 走看看