zoukankan      html  css  js  c++  java
  • bzoj3876: [Ahoi2014]支线剧情

    神犇题解:http://blog.csdn.net/popoqqq/article/details/43024221

    题意:给定一个DAG,1为起始点,任意一个点可以直接回到1,每条边有经过代价,求一种最优方案使得每条边至少经过一次,代价最小。

    无源汇有下界最小费用可行流。

    考虑我们对于无源汇的建图:对于每个点,它的入度下界为容量,S向它拉一条边,出度下界为容量,其向T连一条边,跑一边最大流即可。

    这里我们建图实际上一样,但是加上了费用,具体建图如下:

    对于x->y,费用为z

    S向y建容量为1,费用为z的边(对应无源汇里的入度处理)

    x向y建容量INF,费用为z的边(即自由流)

    对于每一个点x,假设其出度为a

    x向T建容量为a,费用为0的边(出度处理)(注意这里不要加上费用,因为已经在入度处理的时候就加过了)

    x向1建容量INF,费用为0的边(对应题意)

    然后跑最小费用流就可以了。

    膜拜ihopenot大爷。

    实际上有一个很大的建图优化,也是无源汇建图的优化,即合并入出度并且相互抵消。但是对于这道题就不好做,因为有费用,不过可以这么想:既然每一条边都要走,我们就直接把费用抽出来,即答案一开始就为所有边走一次的代价,然后这样入度的费用就变为0了。然后再与出度进行抵消,最后边的数量大大减少。

    实践证明,从原来改到现在,时间从8000多优化到100多ms。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 50005
     4 #define INF 1e9
     5 inline int read(){
     6     int x=0,f=1;char a=getchar();
     7     while(a<'0' || a>'9') {if(a=='-') f=-1; a=getchar();}
     8     while(a>='0' && a<='9') x=x*10+a-'0',a=getchar();
     9     return x*f;
    10 }
    11 int ans,n,cnt,a[N],p[N],d[N],head[N],S,T;
    12 bool vis[N];
    13 queue<int>q;
    14 struct edges{
    15     int fr,to,cap,flow,cost,next;
    16 }e[2*N];
    17 inline void insert(int u,int v,int f,int c){
    18     e[cnt]=(edges){u,v,f,0,c,head[u]};head[u]=cnt++;
    19     e[cnt]=(edges){v,u,0,0,-c,head[v]};head[v]=cnt++;
    20 }
    21 inline bool spfa(){
    22     memset(d,0x3f,sizeof(d));
    23     d[S]=0; a[S]=INF; q.push(S);
    24     while(!q.empty()){
    25         int x=q.front(); q.pop(); vis[x]=0;
    26         for(int i=head[x];i>=0;i=e[i].next)
    27             if(d[e[i].to]>d[x]+e[i].cost && e[i].flow<e[i].cap){
    28                 d[e[i].to]=d[x]+e[i].cost; p[e[i].to]=i;
    29                 a[e[i].to]=min(a[x],e[i].cap-e[i].flow);
    30                 if(!vis[e[i].to]) vis[e[i].to]=1,q.push(e[i].to);
    31             }
    32     }
    33     return d[T]<INF;
    34 }
    35 inline void mincf(){
    36     ans+=a[T]*d[T];
    37     int u=T;
    38     while(u!=S){
    39         e[p[u]].flow+=a[T];
    40         e[p[u]^1].flow-=a[T];
    41         u=e[p[u]].fr;
    42     }
    43 }
    44 int main(){
    45     memset(head,-1,sizeof(head));
    46     n=read(); S=0; T=n+1;
    47     for(int a,i=1;i<=n;i++){
    48         a=read(); insert(i,T,a,0); 
    49         if(i!=1) insert(i,1,INF,0);
    50         for(int to,co,j=1;j<=a;j++)
    51         to=read(),co=read(),insert(i,to,INF,co),insert(S,to,1,co);
    52     }
    53     while(spfa()) mincf();
    54     printf("%d
    ",ans);
    55     return 0;
    56 }

    优化建图:

    1 for(int a,i=1;i<=n;i++){
    2         a=read(); cd[i]=a;
    3         if(i!=1) insert(i,1,INF,0);
    4         for(int to,co,j=1;j<=a;j++)
    5         to=read(),co=read(),ans+=co,r[to]++,insert(i,to,INF,co);
    6     }
    7     for(int i=2;i<=n;i++) 
    8     if(r[i]>cd[i]) insert(S,i,r[i]-cd[i],0);
    9     else insert(i,T,cd[i]-r[i],0);
  • 相关阅读:
    linux 删除乱码文件
    snprintf用法
    面试时经常问到的非技术性问题
    vector查找元素
    new 和delete
    python安装
    UIPickerView详解
    设置文本框左边显示的View
    字符串的分割??
    VC++异常捕获??
  • 原文地址:https://www.cnblogs.com/enigma-aw/p/6228109.html
Copyright © 2011-2022 走看看