zoj 2676 二分+ISAP模板求实型参数的最小割(0-1分数规划问题)(可做ISAP模板)

/*
参考博文:http://www.cnblogs.com/ylfdrib/archive/2010/09/01/1814478.html
以下题解为转载代码自己写的:
zoj2676
胡伯涛论文《最小割模型在信息学竞赛中的应用》中详细介绍了分数规划思想的应用。经典的有最优比率生成树。

对于分数规划的应用中,常用的就是0-1分数规划,即解向量X = {x1, ……,xi, ……}, 对于∀xi∈{0,1}。

主要求解过程是,首先将原分式优化问题,转换成非分式优化问题,利用单调的性质,用二分逼近的方法找到最优解。
题目要求最后能够截得信息,即求某个割,使得c/k最小。

这一题可以将问题转化为最小割,求c/k的最小值,即求sum(xi * ci) / sum(xi * 1)的最小值,(xi == 0  || xi == 1)

设ans = sum(xi * ci) / sum(xi * 1)

则 sum(xi * ci) - ans * sum(xi * 1) = 0

即 sum(xi * (ci - ans)) = 0;

令F(x) = sum(xi * (ci - ans)); 对于一定值ans,函数为单调递减的。

对于正解ANS, F(x) = 0;

则可以推出:

如果 F(x) = 0 那么 ans = ANS

如果 F(x) < 0 那么 ans > ANS

如果 F(x) > 0 那么 ans < ANS

用二分的方法逼近答案,令COST = ci - ans,作为第i条边的新花费,求得最小割ans进行验证即可。
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<math.h>
using namespace std;
#define eps 1e-6//二分1e-10超时
#define inf 0x3fffffff
#define N 410
struct node
{
    int u,v,next;
    double w;
}f[N],bian[N*4];
int yong,head[N],cur[N],q[N],gap[N],dis[N];
void init()
{
    yong=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,double w)
{
    bian[yong].u=u;
    bian[yong].v=v;
    bian[yong].w=w;
    bian[yong].next=head[u];
    head[u]=yong++;
}
void bfs(int start,int endl)//建立到汇点的距离层次图存在dis[]数组中
{
    int rear=0,i,j;
    memset(dis,-1,sizeof(dis));
    memset(gap,0,sizeof(gap));//gap[x]记录dis[i]=x出现了多少次
    dis[endl]=0;
    gap[dis[endl]]=1;
    q[rear++]=endl;
    for(i=0;i<rear;i++)
    {
        for(j=head[q[i]];j!=-1;j=bian[j].next)
        {
            int v=bian[j].v;
            if(dis[v]==-1)
            {
                ++gap[dis[v]=dis[q[i]]+1];
                q[rear++]=v;
            }
        }
    }
}
double SAP(int start,int endl,int n)
{
    double ans=0;
    bfs(start,endl);
    int cur[N];//代替head数组
    memcpy(cur,head,sizeof(head));
    int stack[N],top=0;//建立手工栈
    int u=start,i;
    while(dis[start]<n)
    {
        if(u==endl)//当搜到终点时即找到从原点到汇点的增光路,正常处理即可
        {
            double mini=inf;
            int tep;
            for(i=0;i<top;i++)
            {
                if(mini>bian[stack[i]].w)
                {
                    mini=bian[stack[i]].w;
                    tep=i;
                }
            }
            for(i=0;i<top;i++)
            {
                bian[stack[i]].w-=mini;
                bian[stack[i]^1].w+=mini;
            }
            ans+=mini;
            top=tep;
            u=bian[stack[top]].u;//此时的u为变容量为0的u
        }
        if(dis[u]&&gap[dis[u]-1]==0)//出现了断层,没有增广路
            break;
        for(i=cur[u];i!=-1;i=bian[i].next)//遍历与u相连的未遍历的节点
        {
            int v=bian[i].v;
            if(dis[v]!=-1)
            {
                if(bian[i].w>eps&&dis[u]==dis[v]+1)//层次关系找到允许路径
                    break;
            }
        }
        if(i!=-1)//找到允许弧
        {
            cur[u]=i;
            stack[top++]=i;
            u=bian[i].v;
        }
        else//无允许的路径,修改标号 当前点的标号比与之相连的点中最小的多1
        {
            int mini=n;
            for(i=head[u];i!=-1;i=bian[i].next)
            {
                if(fabs(bian[i].w)<eps)continue;
                int v=bian[i].v;
                if(mini>dis[v])//找到与u相连的v中dep[v]最小的点
                {
                    mini=dis[v];
                    cur[u]=i;//最小标号就是最新的允许弧
                }
            }
            --gap[dis[u]];//dep[u] 的个数变化了 所以修改gap
            ++gap[dis[u]=mini+1];//将dep[u]设为min(dep[v]) + 1, 同时修改相应的gap[]
            if(u!=start)//该点非源点&&以u开始的允许弧不存在,退点
                u=bian[stack[--top]].u;
        }
    }
    return ans;
}
int a[N],len,vis[N];
void dfss(int u) {
  int i;
  // printf("%d
",u);
  for(i=head[u];i!=-1;i=bian[i].next) {
    int v=bian[i].v;
    if(bian[i].w>eps&&!vis[v]) {
        vis[v]=1;
        dfss(v);
    }
  }
}
int main()
{
    int n,m,i;
    double st,en,mid,flow;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1; i<=m; i++)
            scanf("%d%d%lf",&f[i].u,&f[i].v,&f[i].w);
        st=0;
        en=10000001;
        while(st<en+eps)
        {
            mid=(st+en)/2;
            init();
            flow=0;
            for(i=1; i<=m; i++)
            {
             //   printf("%.2f %.2f
",f[i].w,mid);
                if(f[i].w<mid+eps)
                    flow=flow+f[i].w-mid;
                else
                {
                    addedge(f[i].u,f[i].v,f[i].w-mid);
                    addedge(f[i].v,f[i].u,f[i].w-mid);
                }
            }
            double k=SAP(1,n,n);
            flow+=k;
      //  printf("%.2f
",k);
            if(flow>eps)
                st=mid+eps;
            else
                en=mid-eps;
        }
        //printf("mid=%.10f
",mid);
        memset(vis,0,sizeof(vis));
        vis[1]=1;
        dfss(1);
        len=0;
            for(i=1;i<=m;i++) {
         //   printf("%.10f %.10f
",f[i].w,mid+eps);//&&vis[f[i].u]+vis[f[i].v]==1
            if(vis[f[i].u]+vis[f[i].v]==1||f[i].w<eps+mid)//如果是负边或者割边
                len++;
            }
            printf("%d
",len);
            int ok=0;
            for(i=1;i<=m;i++)
            if(vis[f[i].u]+vis[f[i].v]==1||f[i].w<mid+eps) {//如果是负边或者割边
                    if(ok)
                    printf(" ");
                printf("%d",i);
            ok=1;
            }
            printf("
");
            }
    return 0;
}

原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410561.html