poj 2125 Destroying The Graph (最小点权覆盖)
Destroying The Graph
Time Limit: 2000MS | Memory Limit: 65536K | |||
Description
Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that Bob tries to destroy it. In a move he may take any vertex of the graph and remove either all arcs incoming into this vertex, or all arcs outgoing from this vertex.
Alice assigns two costs to each vertex: Wi+ and Wi-. If Bob removes all arcs incoming into the i-th vertex he pays Wi+ dollars to Alice, and if he removes outgoing arcs he pays Wi- dollars.
Find out what minimal sum Bob needs to remove all arcs from the graph.
Alice assigns two costs to each vertex: Wi+ and Wi-. If Bob removes all arcs incoming into the i-th vertex he pays Wi+ dollars to Alice, and if he removes outgoing arcs he pays Wi- dollars.
Find out what minimal sum Bob needs to remove all arcs from the graph.
Input
Input file describes the graph Alice has drawn. The first line of the input file contains N and M (1 <= N <= 100, 1 <= M <= 5000). The second line contains N integer numbers specifying Wi+. The third line defines Wi- in a similar way. All costs are positive and do not exceed 106 . Each of the following M lines contains two integers describing the corresponding arc of the graph. Graph may contain loops and parallel arcs.
Output
On the first line of the output file print W --- the minimal sum Bob must have to remove all arcs from the graph. On the second line print K --- the number of moves Bob needs to do it. After that print K lines that describe Bob's moves. Each line must first contain the number of the vertex and then '+' or '-' character, separated by one space. Character '+' means that Bob removes all arcs incoming into the specified vertex and '-' that Bob removes all arcs outgoing from the specified vertex.
Sample Input
3 6 1 2 3 4 2 1 1 2 1 1 3 2 1 2 3 1 2 3
Sample Output
5 3 1 + 2 - 2 +
题目大意:
n个点m条边的有向图
需要移走这张图里所有的边
每次可以选择移走点i的所有入边或所有出边
每步操作都有对应的代价
求最小代价移走所有的边
注:边有自环和平行边
最小点权覆盖集
=最小割
拆点
源点向每个点连一条流量为outgoing pay的边
每个点向汇点连一条流量为incoming pay的边
原图中的边i,j,由i向拆出的j连inf边
跑最小割
方案的输出:
从源点遍历残量网络,边还有流量就遍历,记录所有遍历到的点
原本就有的点,如果没有被遍历到,就说明它被割了
拆出的点,如果被遍历到,说明它被割了
#include<cstdio> #include<queue> using namespace std; int n,m,tot=1,ans; int front[11100],to[11100],nextt[11100],cap[11100]; int lev[210],cur[210]; int src,decc; bool g[210]; queue<int>q; void add(int u,int v,int w) { to[++tot]=v;nextt[tot]=front[u];front[u]=tot;cap[tot]=w; to[++tot]=u;nextt[tot]=front[v];front[v]=tot;cap[tot]=0; } bool bfs() { for(int i=0;i<=decc;i++) {lev[i]=-1;cur[i]=front[i];} while(!q.empty()) q.pop(); q.push(src);lev[src]=0; while(!q.empty()) { int now=q.front();q.pop(); for(int i=front[now];i;i=nextt[i]) { int t=to[i]; if(cap[i]>0&&lev[t]==-1) { lev[t]=lev[now]+1; q.push(t); if(t==decc) return true; } } } return false; } int dinic(int now,int flow) { if(now==decc) return flow; int rest=0,delta; for(int & i=cur[now];i;i=nextt[i]) { int t=to[i]; if(lev[t]>lev[now]&&cap[i]>0) { delta=dinic(t,min(flow-rest,cap[i])); if(delta) { cap[i]-=delta;cap[i^1]+=delta; rest+=delta;if(rest==flow) break; } } } if(rest!=flow) lev[now]=-1; return rest; } void cut(int now) { g[now]=true; for(int i=front[now];i;i=nextt[i]) { if(cap[i]==0||g[to[i]]) continue; cut(to[i]); } } int main() { scanf("%d%d",&n,&m); decc=n+1<<1; int x,y; for(int i=1;i<=n;i++) { scanf("%d",&x); add(i<<1|1,decc,x); } for(int i=1;i<=n;i++) { scanf("%d",&x); add(src,i<<1,x); } for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x<<1,y<<1|1,2e9); } while(bfs()) ans+=dinic(src,2e9); printf("%d\n",ans); int sum=0; cut(src); for(int i=1;i<=n;i++) { if(g[i<<1|1]) sum++; if(!g[i<<1]) sum++; } printf("%d\n",sum); for(int i=1;i<=n;i++) { if(!g[i<<1]) printf("%d -\n",i); if(g[i<<1|1]) printf("%d +\n",i); } }
错误:
1、
应该是
源点向每个点连一条流量为outgoing pay的边
每个点向汇点连一条流量为incoming pay的边
连反了
与源点相连的点,连出去的边是点打出的,所以源点与点之间的边控制的是出边的流量
汇点同理
2、方案输出方法错误
错误方法:
在残量网络中,如果与源点相连的边流量为0,说明这个点被割了
如果汇点连出去的边的流量 为这条边指 向的点的原流量,说明这个点被割了
前半部分是正确的,但后半部分是错的
因为跑最大流过程中,增光路上所有边流量都减,
比如有一条边由1指向2,所有花费都是1
跑完最大流后,源点——1 残量为0
2——汇点 残量为0
最终判断的是割掉2个点,但实际割其中一个就行
/*for(int i=front[src];i;i=nextt[i])
{
if(cap[i]==0)
{
sum++;
a[sum][0]=to[i]/2;a[sum][1]='+';
}
}
for(int i=front[decc];i;i=nextt[i])
{
if(cap[i]==out[to[i]/2])
{
sum++;
a[sum][0]=to[i]/2;a[sum][1]='-';
}
}*/
3、题目中说有自环,做的时候把它特判去掉了,错