01分数规划

bzoj1486 最小圈

题目大意:求一个图内的某个环,使得sigma ai[i]/k(环上点数)最小。

思路:二分答案,如果sigma ai[i]-k*mid>0说明mid可以更大,每次判断的时候给所有边-mid,就成了判断负环的问题。这里用spfa普通的判法会tle,所以要用深搜版的spfa来判断。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxm 3005
#define maxe 20005
#define eps 1e-10
using namespace std;
int n,point[maxm]={0},next[maxe]={0},en[maxe]={0},tot=0;
double va[maxe],vv[maxe]={0},dis[maxm];
bool visit[maxm],flag;
int cmp(double x,double y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;
}
void add(int u,int v,double w){
    next[++tot]=point[u];point[u]=tot;en[tot]=v;vv[tot]=w;
}
void spfa(int u){
    int i,j,v;visit[u]=true;
    for (i=point[u];i;i=next[i]){
        if (dis[v=en[i]]>dis[u]+va[i]){
            if (visit[v]){flag=true;break;}
            else{
                dis[v]=dis[u]+va[i];spfa(v);
            }
        }
    }visit[u]=false;
}
bool judge(double x){
    int i,j;flag=false;
    memset(dis,0,sizeof(dis));
    memset(visit,false,sizeof(visit));
    for (i=1;i<=tot;++i) va[i]=vv[i]-x;
    for (i=1;i<=n;++i){
        spfa(i);if (flag) return true;
    }return false;
}
int main(){
    int i,j,m,u,v;double w,l=0.0,r=0.0,mid;
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;++i){
        scanf("%d%d%lf",&u,&v,&w);
        add(u,v,w*1.0);r=max(r,w*1.0);l=min(l,w*1.0);
    }while(cmp(l,r)<0){
        mid=(l+r)/2;
        if (judge(mid))r=mid;
        else l=mid;
    }printf("%.8f\n",l);
}
View Code

 

bzoj3232 圈地游戏

题目大意:给定一个网格,格内和边上都有权值,求一个圈,使得格内的权值和v/边上的权值和c最大。

思路:二分答案k,判断能否存在v-ck>0,就是求所有格内权值-不选的格的权值-边界上选的格在边上的权值-选的两个格间的边权,然后就是总的格内的价值-最小割,S表示选,T表示不选,那么从S向边界的点连边界的边权,相邻两点连中间边权,每个点向T连格内边权。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxm 5000
#define maxe 100000
#define maxx 55
#define inf 2100000000.0
#define eps 1e-9
using namespace std;
struct use{
    int st,en;double va;
}edge[maxe];
double ai[maxx][maxx],he[maxx][maxx],li[maxx][maxx],sum=0;
int point[maxm],next[maxe],dis[maxm],gap[maxm],pre[maxm],bi[maxx][maxx],tot,n,m,st,en,
    dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},cur[maxm];
void add(int u,int v,double vv){
    next[++tot]=point[u];point[u]=tot;edge[tot]=(use){u,v,vv};
    next[++tot]=point[v];point[v]=tot;edge[tot]=(use){v,u,0.0};
}
double isap(){
    int i,j,u,v;bool f;double ans=0,minn;
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for (i=st;i<=en;++i) cur[i]=point[i];
    gap[0]=en-st+1;u=st;
    while(dis[st]<en-st+1){
        f=false;
        for (i=cur[u];i;i=next[i])
            if (edge[i].va-0>eps&&dis[edge[i].en]+1==dis[u]){
                cur[u]=i;f=true;break;
            }
        if (f){
            pre[u=edge[i].en]=i;
            if (u==en){
                minn=inf;
                for (i=en;i!=st;i=edge[pre[i]].st)
                    minn=min(minn,edge[pre[i]].va);
                ans+=minn;
                for (i=en;i!=st;i=edge[pre[i]].st){
                    edge[pre[i]].va-=minn;
                    edge[pre[i]^1].va+=minn;
                }u=st;
            }
        }else{
            --gap[dis[u]];
            if (!gap[dis[u]]) return ans;
            j=en-st+1;
            for (i=point[u];i;i=next[i])
                if (edge[i].va-0>eps)
                  j=min(j,dis[edge[i].en]);
            ++gap[dis[u]=j+1];cur[u]=point[u];
            if (u!=st) u=edge[pre[u]].st;
        }
    }return ans;
}
bool judge(double x){
    int i,j,k,xi,yi;tot=1;
    memset(point,0,sizeof(point));
    memset(next,0,sizeof(next));
    for (i=1;i<=m;++i){
        add(st,bi[1][i],x*he[1][i]);
        add(st,bi[n][i],x*he[n+1][i]);
    }for (i=1;i<=n;++i){
        add(st,bi[i][1],x*li[i][1]);
        add(st,bi[i][m],x*li[i][m+1]);
    }for (i=1;i<=n;++i)
      for (j=1;j<=m;++j) add(bi[i][j],en,ai[i][j]);
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j)
        for (k=0;k<4;++k){
            xi=i+dx[k];yi=j+dy[k];
            if (xi<1||xi>n||yi<1||yi>m) continue;
            if (k==0) add(bi[i][j],bi[xi][yi],x*li[i][j+1]);
            if (k==1) add(bi[i][j],bi[xi][yi],x*he[i+1][j]);
            if (k==2) add(bi[i][j],bi[xi][yi],x*li[i][j]);
            if (k==3) add(bi[i][j],bi[xi][yi],x*he[i][j]);
        }return (sum-isap()>eps);
}
int main(){
    int i,j;double l,r,mid;scanf("%d%d",&n,&m);
    st=tot=1;en=n*m+2;
    for (i=1;i<=n;++i)
      for (j=1;j<=m;++j){scanf("%lf",&ai[i][j]);bi[i][j]=++tot;sum+=ai[i][j];}
    for (i=1;i<=n+1;++i)
      for (j=1;j<=m;++j) scanf("%lf",&he[i][j]);
    for (i=1;i<=n;++i)
      for (j=1;j<=m+1;++j) scanf("%lf",&li[i][j]);
    l=0.0;r=250000.0;
    while(r-l>eps){
        mid=(l+r)/2.0;
        if (judge(mid)) l=mid;
        else r=mid;
    }printf("%.3f\n",l);
}
View Code

 

bzoj2285 保密

题目大意:给定n个点和m条有向边(有时间ti和安全系数si),点是两排点,前n1个点间有一些连接两排点的空腔,一条路径的安全度是sigma(ti)/sigma(si),如果一只军队能从n到一个点,就能访问过所有这个点连出去的空腔,求访问过所有空腔的最小的安全度和。

思路:分数规划+网络流。对于每个有空腔连接的点,分数规划求出从n到每一个点的最小安全度(二分答案mid,最短路,sigma(ti)-midsigma(si)<0,可以用map存一下每一种二分的mid,算过就不用算了,这样可以节省计算量)。然后就是从两排点中选出一些点覆盖所有的空腔且权值和最小,这是最小点权覆盖的模型,从s向第一排点连边权值,从第二排向终点连边权值,将空腔所连接的两个点连边inf,求出最小割,如果大于inf就是无解,否则就输出。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define maxm 705
#define maxe 1000005
#define eps 1e-5
#define LD double
#define inf 2100000000.
#define len 1000000
using namespace std;
struct use{int st,en;double va,si;}edge[maxe];
int point[maxm]={0},next[maxe],tot,gap[maxm]={0},dis[maxm]={0},pre[maxm]={0},n,que[len+1],
    st,en,cur[maxm],tt=0;
LD dq[maxm],di[10005][maxm];
bool visit[maxm]={false},vi[maxm]={false};
map<double,int>cnt;
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;
}
void add(int u,int v,LD vv,LD va){
    next[++tot]=point[u];point[u]=tot;edge[tot]=(use){u,v,vv,va};
}
void add2(int u,int v,LD vv){
    next[++tot]=point[u];point[u]=tot;edge[tot]=(use){u,v,vv,0.};
    next[++tot]=point[v];point[v]=tot;edge[tot]=(use){v,u,0.,0.};
}
LD spfa(int y,LD x){
    int i,j,u,v,head=0,tail;
    di[y][que[tail=1]=n]=0.;visit[n]=true;
    while(head!=tail){
        visit[u=que[head=head%len+1]]=false;
        for (i=point[u];i;i=next[i]){
            if (di[y][v=edge[i].en]>di[y][u]+edge[i].va-x*edge[i].si){
                di[y][v]=di[y][u]+edge[i].va-x*edge[i].si;
                if (!visit[v]) visit[que[tail=tail%len+1]=v]=true;
            }
        }
    }
}
bool judge(int x,LD y){
    int i=cnt[y];
    if (!i) spfa(i=cnt[y]=++tt,y);
    return cmp(di[i][x],0.)<=0;
}
LD isap(){
    int i,j,u,v,minn;LD mn,ans=0.;bool f;
    gap[0]=en-st+1;u=st;
    for (i=st;i<=en;++i) cur[i]=point[i];
    while(dis[st]<=en-st+1){
        f=false;
        for (i=cur[u];i;i=next[i])
          if (edge[i].va>eps&&dis[edge[i].en]+1==dis[u]){
            cur[u]=i;f=true;break;
          }
        if (f){
            pre[u=edge[i].en]=i;
            if (u==en){
                for (mn=inf,i=en;i!=st;i=edge[pre[i]].st)
                  mn=min(mn,edge[pre[i]].va);
                ans+=mn;
                for (i=en;i!=st;i=edge[pre[i]].st){
                    edge[pre[i]].va-=mn;
                    edge[pre[i]^1].va+=mn;
                }u=st;
            }
        }else{
            if (!(--gap[dis[u]])) return ans;
            minn=en-st+1;
            for (cur[u]=i=point[u];i;i=next[i])
                if (edge[i].va>eps) minn=min(minn,dis[edge[i].en]);
            ++gap[dis[u]=minn+1];
            if (u!=st) u=edge[pre[u]].st;
        }
    }return ans;
}
int main(){
    int m,i,j,n1,m1,u,v,vv;LD t,s,l,r,mid;
    scanf("%d%d",&n,&m);
    for (tot=0,i=1;i<=m;++i){
        scanf("%d%d%lf%lf",&u,&v,&t,&s);
        add(u,v,t,s);
    }memset(di,127,sizeof(di));
    spfa(cnt[++tt]=1,0.);
    tot=1;scanf("%d%d",&m1,&n1);
    for (i=1;i<=n1;++i) vi[i]=(cmp(di[1][i],di[0][0])<0);
    for (i=n1;i;--i){
        if (!vi[i]){dq[i]=inf;continue;}
        l=0.;r=10.;
        while(r-l>eps){
            mid=(l+r)/2.;
            if (judge(i,mid)) r=mid;
            else l=mid;
        }dq[i]=l;
    }memset(point,0,sizeof(point));
    st=0;en=n1+1;
    for (i=1;i<=n1;i+=2) add2(st,i,dq[i]);
    for (i=2;i<=n1;i+=2) add2(i,en,dq[i]);
    for (i=1;i<=m1;++i){
        scanf("%d%d",&u,&v);add2(u,v,inf);
    }t=isap();
    if (t<inf) printf("%.1f\n",t);
    else printf("-1\n");
}
View Code

 

bzoj3597 方伯伯运椰子

题目大意;给出一个有向无环图,已知每条路的参数ai、bi、ci、di,表示压缩/扩容一流量的费用,当前流量和流过1流量的费用。求(X-Y)/k的最大值,其中X指改变前费用、Y是改变后的,k是改变次数,不能改变起点出发的边,要求改变前后的总流量不变,且每条边都满流。

思路:0/1分数规划+spfa判负环。考虑二分答案mid,如果(X-Y)-k*mid>0,就说明答案可以更大,变换一下就是(Y-X)+k*mid<0。对于给定的满流的网络,可以建图:u->v:bi+di+mid的边,表示扩容;如果c>0,说明可以压缩,v->u:ai-di+mid的边。从起点连出去的边不能改变,所以只建u->v,0的边就可以了。因为<0,所以找负环就可以了。

注意:1)不是找从起点到终点的负路径,因为要满足流量的要求,所以一定是一个环的某些边+1,某些边-1。

     2)只对c>0的边建反向边,表示可以压缩。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5005
#define M 6005
#define LD double
#define eps 1e-9
#define epe 1e-4
#define inf 1e50
#define len 100000
using namespace std;
int in(){
    char ch=getchar();int x=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x;}
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;}
struct use{int u,v,ai,bi,ci,di;}ed[M];
int n,m,point[N],next[M],en[M],tot,que[len+1],vz[N];
LD va[M],dis[N];
bool vi[N];
void add(int i,LD x){
    int u,v;u=ed[i].u;v=ed[i].v;
    next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=(LD)(ed[i].bi+ed[i].di)+x;
    if (!ed[i].ci) return;
    next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=(LD)(ed[i].ai-ed[i].di)+x;
}
bool spfa(){
    int i,u,v,head=0,tail;
    for (i=n+2;i;--i){dis[i]=inf;vi[i]=false;vz[i]=0;}
    vi[que[tail=1]=n+1]=true;++vz[n+1];dis[n+1]=0.;
    while(head!=tail){
        vi[u=que[head=head%len+1]]=false;
        for (i=point[u];i;i=next[i]){
            if (cmp(dis[v=en[i]],dis[u]+va[i])>0){
                dis[v]=dis[u]+va[i];
                if (!vi[v]){
                    vi[que[tail=tail%len+1]=v]=true;
                    ++vz[v];
                    if (vz[v]>n) return true;
                }
            }
        }
    }return false;
}
bool judge(LD x){
    int i;tot=0;
    memset(point,0,sizeof(point));
    for (i=1;i<=m;++i){
        if (ed[i].u==n+1){
            next[++tot]=point[ed[i].u];point[ed[i].u]=tot;en[tot]=ed[i].v;va[tot]=0.;
        }else add(i,x);
    }return spfa();
}
int main(){
    int i;LD l,r,mid;
    n=in();m=in();l=r=0.;
    for (i=1;i<=m;++i){
        ed[i]=(use){in(),in(),in(),in(),in(),in()};
        r=(LD)ed[i].di*(LD)ed[i].ci;
    }while(r-l>epe){
        mid=(l+r)/2.;
        if (judge(mid)) l=mid;
        else r=mid;
    }printf("%.2f\n",l);
}
View Code

 

posted @ 2015-10-27 23:23  Rivendell  阅读(367)  评论(0编辑  收藏  举报