神犇题解: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\n",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);