题意:
有N个城市,M条单向路,Tom想环游全部城市,每次至少环游2个城市,每个城市只能被环游一次。由于每条单向路都有长度,要求游遍全部城市的最小长度。
// 给定一个有向图,必须用若干个环来覆盖整个图,要求这些覆盖的环的权值最小。
XD 第一次知道网络流拆点做法 但还没完全理解透,多做几题感悟感悟 ,先发一下别人题解 ,代码当然自己写
思路:
原图每个点 u 拆为 u 和 u' ,从源点引容量为 1 费用为 0 的边到 u ,从 u' 引相同性质的边到汇点,若原图中存在 (u, v) ,则从 u 引容量为 1 费用为 c(u, v) 的边到 v' 。
这里源模拟的是出度,汇模拟的是入度,又每个点的出度等于入度等于 1 ,那么如果最大流不等于顶点数 n ,则无解;否则,答案就是最小费用。
对于满足条件的环,每个点的入度和出度均为1,我们可以把每个点拆成入点和出点,那么也就是说一个入点对应一个出点,一个出点对应一个入点。那么这个问题就变成了一个最佳匹配问题。
代码:
#include#include #include #include #include #include #include #include #include #include #define oo 0x13131313using namespace std;const int MAXN=300;const int MAXM=300000;const int INF=0x3f3f3f3f;struct Edge{ int to,next,cap,flow,cost; void get(int a,int b,int c,int d) { to=a,cap=b,cost=c;next=d;flow=0; }}edge[MAXM];int head[MAXN],tol;int pre[MAXN],dis[MAXN];bool vis[MAXN];int N;void init(int n){ N=n; tol=0; memset(head,-1,sizeof(head));}void addedge(int u,int v,int cap,int cost){ edge[tol].get(v,cap,cost,head[u]);head[u]=tol++; edge[tol].get(u,0,-cost,head[v]);head[v]=tol++;}bool spfa(int s,int t){ queue q; for(int i=0;i edge[i].flow&& dis[v]>dis[u]+edge[i].cost ) { dis[v]=dis[u]+edge[i].cost; pre[v]=i; if(!vis[v]) { vis[v]=true; q.push(v); } } } } if(pre[t]==-1) return false; else return true;}int minCostMaxflow(int s,int t,int &cost){ int flow=0; cost = 0; while(spfa(s,t)) { int Min=INF; for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]) { if(Min >edge[i].cap-edge[i].flow) Min=edge[i].cap-edge[i].flow; } for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]) { edge[i].flow+=Min; edge[i^1].flow-=Min; cost+=edge[i].cost*Min; } flow+=Min; } return flow;}int NN,MM;void input(){ int a,b,c; for(int i=1;i<=MM;i++) { scanf("%d%d%d",&a,&b,&c); addedge(a,b+NN,1,c); }}void solve(){ int s=NN*2+1,t=NN*2+2,k,ANS; for(int i=1;i<=NN;i++) { addedge(s,i,1,0); addedge(i+NN,t,1,0); } k=minCostMaxflow(s,t,ANS); if(k==NN) printf("%d\n",ANS); else printf("-1\n");}void File(){ freopen("a.in","r",stdin); freopen("a.out","w",stdout);}int main(){ // File(); while(cin>>NN>>MM) { init(MAXN); input(); solve(); } return 0;}