zoj 2676 网络流+01分数规划

思路:

这题的结论得要看amber的论文,结论就是将求f(x)/b(x)最小转化为求min(f(x)-b(x)*λ),其中x为S集的解空间,f(x)为解的边权和,b(x)为解的边数,

λ=f(x)/b(x)。λ*为最优解,当且仅当(x属于S)∑min(f(x)-b(x)*λ)==0;故可以将原边权的权值改为w-λ;对λ进行二分枚举,找出答案。

#include<cstdio>

#include<iostream>

#include<cstring>

#include<algorithm>

#include<vector>

#define N 510

#define M 50010

#define inf 1e9

using namespace std;

const double eps=1e-9;

struct Edge{

    int to,next,from;

    double val;

}edge[M];

int Index[N],d[N],gap[N],e,vi[N];

void addedge(int from,int to,double val)

{

    edge[e].from=from;

    edge[e].to=to;

    edge[e].val=val;

    edge[e].next=Index[from];

    Index[from]=e++;

    edge[e].from=to;

    edge[e].to=from;

    edge[e].val=val;

    edge[e].next=Index[to];

    Index[to]=e++;

}

int source,des,n,m;

void DFS(int u)

{

    vi[u]=1;

    int i,v;

    for(i=Index[u];i!=-1;i=edge[i].next)

        if(edge[i].val&&!vi[edge[i].to])

            DFS(edge[i].to);

}

double dfs(int pos,double flow)

{

    if(pos==des)

        return flow;

    int i,j,v,mind;

    double val,c,lv;

    mind=n-1;//初始最小标号为n-1

    lv=flow;

    for(i=Index[pos];i!=-1;i=edge[i].next)

    {

        v=edge[i].to;

        val=edge[i].val;

        if(val)

        {

            if(d[v]+1==d[pos])

            {

                c=min(lv,val);//对于该点的最小可行流

                c=dfs(v,c);

                edge[i].val-=c;//更新剩余图

                edge[i^1].val+=c;

                lv-=c;

                if(d[source]>=n)return flow-lv;

                if(lv==0) break;

            }

            if(d[v]<mind)mind=d[v];//找出与pos相连的点的最小标号

        }

    }

    if(lv==flow)//没有找到增广路劲,进行标号更新

    {

        --gap[d[pos]];

        if(!gap[d[pos]])

            d[source]=n;

        d[pos]=mind+1;

        ++gap[d[pos]];

    }

    return flow-lv;

}

double sap(int st,int de)

{

    source=st;

    des=de;

    memset(d,0,sizeof(d));

    memset(gap,0,sizeof(gap));

    gap[0]=n;//初始标号为0的有n个.

    double ans=0;

    while(d[st]<n)

    {

        ans+=dfs(st,inf);

        //cout<<d[st]<<endl;

    }

    return ans;

}

void init()

{

    e=0;

    memset(Index,-1,sizeof(Index));

    memset(vi,0,sizeof(vi));

}

int main()

{

    int i,j,a[N],b[N],w[N];

    int lmin,rmax;

    int ff=0;

    while(scanf("%d%d",&n,&m)!=EOF)

    {

        if(ff)printf("\n");

        ff=1;

        init();

        lmin=inf;

        rmax=0;

        for(i=0;i<m;i++)

        {

            scanf("%d%d%d",a+i,b+i,w+i);

            lmin=min(lmin,w[i]);

            rmax=max(rmax,w[i]);

        }

        double l,r;

        l=lmin,r=rmax;

        double res=0;

        while(r-l>eps)

        {

            init();

            double mid=(l+r)/2;

            res=0;

            for(i=0;i<m;i++)

            {

                if(w[i]<=mid) res+=w[i]-mid;

                else addedge(a[i],b[i],w[i]-mid);

            }

            res+=sap(1,n);

            if(res>0)

                l=mid;

            else

                r=mid;

        }

        init();

        for(i=0;i<m;i++)

        {

            if(w[i]<=r) continue;

            addedge(a[i],b[i],w[i]-r);

        }

        sap(1,n);

        //cout<<r<<endl;

        //cout<<vi[2]<<" "<<vi[3]<<" "<<vi[4]<<" "<<vi[5]<<endl;

        DFS(1);

        vector<int> ans;

        for(i=0;i<m;i++)

        {

            if((w[i]<=r)||(vi[a[i]]&&!vi[b[i]])||(!vi[a[i]]&&vi[b[i]]))

            {

                ans.push_back(i+1);

                //cout<<edge[i].from<<" "<<edge[i].to<<endl;

            }

        }

        int temp=ans.size();

        printf("%d\n",temp);

        printf("%d",ans[0]);

        for(i=1;i<temp;i++)

            printf(" %d",ans[i]);

        printf("\n");

    }

    return 0;

}

 

你可能感兴趣的:(网络流)