zoukankan      html  css  js  c++  java
  • 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;
    }
    

  • 相关阅读:
    DHCP三种配置
    Linux-9引导过程与服务控制
    Linux系统管理10-进程和计划任务管理
    Linux系统管理11-系统安全及应用
    Linux系统管理12-Linux文件系统与日志
    ---01--Lin网络基础设置
    服务器RAID及磁盘配额的配置
    Linux常见故障及修复方法
    作业5
    104 权限 sudo 解压缩
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410561.html
Copyright © 2011-2022 走看看