题目大概说,给一张图,删除其中一些单向边,使起点s出度比入度多1,终点t入度比出度多1,其他点出度等于入度。其中删除边的费用是bi,保留边的费用是ai,问完成要求最小的费用是多少。
一开始我想到和混合图欧拉回路(POJ1637)的类似构造方法:
- 假设所有边一开始都是保留的,算出各个点的入度和出度,另外s点的出度-1,t点的入度-1;
- 然后把出度-入度等于正数的点源点向其连一条容量为出度-入度的边,等于负数的点其向汇点连一条容量为入度-出度的边
- 这样就是要通过删除边使那些与源汇相连的边满流,这样就满足各个点度的要求;而删除一条边<u,v>会使u的出度-1,v的入度-1,这样在容量网络中由u向v连容量1费用bi-ai的边
- Σai-MCMF就是答案
不过这样是错的= =而且因为负环死循环TLE了。我试图想拆点消除负环,发现两端点都与源点或汇点相连的边都不起作用,觉得怪怪的好像不大对,提交果然是WA的。
正确的做法是:一开始不是假设所有边都是保留的,而是贪心地先确定最少的费用,即如果ai<bi则保留否则删除!
然后接下去的做法也是类似的,我就不赘述了。。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 #define INF (1<<30) 7 #define MAXN 111 8 #define MAXM 8888 9 struct Edge{ 10 int u,v,cap,cost,next; 11 }edge[MAXM]; 12 int vs,vt,NV,NE,head[MAXN]; 13 void addEdge(int u,int v,int cap,int cost){ 14 edge[NE].u=u; edge[NE].v=v; edge[NE].cap=cap; edge[NE].cost=cost; 15 edge[NE].next=head[u]; head[u]=NE++; 16 edge[NE].u=v; edge[NE].v=u; edge[NE].cap=0; edge[NE].cost=-cost; 17 edge[NE].next=head[v]; head[v]=NE++; 18 } 19 int d[MAXN],pre[MAXN]; 20 bool vis[MAXN]; 21 bool SPFA(){ 22 for(int i=0; i<NV; ++i){ 23 d[i]=INF; vis[i]=0; 24 } 25 d[vs]=0; vis[vs]=1; 26 queue<int> que; 27 que.push(vs); 28 while(!que.empty()){ 29 int u=que.front(); que.pop(); 30 for(int i=head[u]; i!=-1; i=edge[i].next){ 31 int v=edge[i].v; 32 if(edge[i].cap && d[v]>d[u]+edge[i].cost){ 33 d[v]=d[u]+edge[i].cost; 34 pre[v]=i; 35 if(!vis[v]){ 36 vis[v]=1; 37 que.push(v); 38 } 39 } 40 } 41 vis[u]=0; 42 } 43 return d[vt]!=INF; 44 } 45 int tot; 46 int MCMF(){ 47 int res=0,mxflow=0; 48 while(SPFA()){ 49 int flow=INF,cost=0; 50 for(int u=vt; u!=vs; u=edge[pre[u]].u){ 51 flow=min(flow,edge[pre[u]].cap); 52 } 53 mxflow+=flow; 54 for(int u=vt; u!=vs; u=edge[pre[u]].u){ 55 edge[pre[u]].cap-=flow; 56 edge[pre[u]^1].cap+=flow; 57 cost+=flow*edge[pre[u]].cost; 58 } 59 res+=cost; 60 } 61 if(tot!=mxflow) return INF; 62 return res; 63 } 64 int u[2222],v[2222],w1[2222],w2[2222]; 65 int deg[MAXN]; 66 int main(){ 67 int T,n,m,s,t; 68 scanf("%d",&T); 69 for(int cse=1; cse<=T; ++cse){ 70 scanf("%d%d%d%d",&n,&m,&s,&t); 71 int res=0; 72 memset(deg,0,sizeof(deg)); 73 --deg[s]; ++deg[t]; 74 for(int i=0; i<m; ++i){ 75 scanf("%d%d%d%d",u+i,v+i,w1+i,w2+i); 76 if(w1[i]<w2[i]){ 77 ++deg[u[i]]; 78 --deg[v[i]]; 79 res+=w1[i]; 80 }else{ 81 res+=w2[i]; 82 } 83 } 84 tot=0; 85 vs=0; vt=n+1; NV=vt+1; NE=0; 86 memset(head,-1,sizeof(head)); 87 for(int i=1; i<=n; ++i){ 88 if(deg[i]>0) addEdge(vs,i,deg[i],0),tot+=deg[i]; 89 else addEdge(i,vt,-deg[i],0); 90 } 91 for(int i=0; i<m; ++i){ 92 if(w1[i]<w2[i]){ 93 addEdge(u[i],v[i],1,w2[i]-w1[i]); 94 }else{ 95 addEdge(v[i],u[i],1,w1[i]-w2[i]); 96 } 97 } 98 int tmp=MCMF(); 99 if(tmp==INF) printf("Case %d: impossible ",cse); 100 else printf("Case %d: %d ",cse,res+tmp); 101 } 102 return 0; 103 }