题意:给定一个有向带权图,找若干个环,使得每个点属于且仅属于一个环,要求使得环权值之和最小
题解:发现这题中每个点属于且仅属于一个环,这时候“仅”这种恰好的含义,让我们想到了匹配问题
当每一个点有且只有一个后继之时,会满足题目的要求,于是把点i拆成i和i',每条边由x连向y',这样做一下二分图最优完美匹配即可
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<queue> #include<vector> #define MAXN 5005 #define ll long long #define INF 0x7f7f7f7f using namespace std; struct Edge{ int from,to,cap,flow,cost; Edge(int u=0,int v=0,int c=0,int f=0,int w=0){ from=u,to=v,cap=c,flow=f,cost=w; } }; struct MCMF{ int n,m,s,t; vector<Edge> edges; vector<int> G[MAXN]; int d[MAXN]; int p[MAXN]; int b[MAXN]; int a[MAXN]; void init(int n,int s,int t){ this->n=n; this->s=s,this->t=t; edges.clear(); for(int i=0;i<=n;i++){ G[i].clear(); } } void AddEdge(int x,int y,int cap,int cost){ edges.push_back(Edge(x,y,cap,0,cost)); edges.push_back(Edge(y,x,0,0,-cost)); m=edges.size(); G[x].push_back(m-2); G[y].push_back(m-1); } int SPFA(int &flow,ll &cost){ memset(d,0x7f,sizeof(d)); memset(b,0,sizeof(b)); queue<int> q; p[s]=0; a[s]=INF; d[s]=0; q.push(s); b[s]=1; while(!q.empty()){ int x=q.front();q.pop(); b[x]=0; for(int i=0;i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(e.cap>e.flow&&d[e.to]>d[x]+e.cost){ p[e.to]=G[x][i]; a[e.to]=min(a[x],e.cap-e.flow); d[e.to]=d[x]+e.cost; if(!b[e.to]){ b[e.to]=1; q.push(e.to); } } } } if(d[t]==INF){ return 0; } flow+=a[t]; cost+=1LL*d[t]*a[t]; for(int i=t;i!=s;i=edges[p[i]].from){ edges[p[i]].flow+=a[t]; edges[p[i]^1].flow-=a[t]; } return 1; } pair<int,ll> MincostMaxflow(){ int flow=0;ll cost=0; while(SPFA(flow,cost)); return make_pair(flow,cost); } }D; int n; int main() { while(1){ scanf("%d",&n); if(!n)break; D.init(n<<1,0,n<<1|1); for(int i=1;i<=n;i++){ D.AddEdge(0,i,1,0); D.AddEdge(n+i,n<<1|1,1,0); } for(int i=1;i<=n;i++){ while(1){ int x;scanf("%d",&x); if(!x)break; int d;scanf("%d",&d); D.AddEdge(i,x+n,1,d); } } pair<int,ll> ans=D.MincostMaxflow(); if(ans.first==n){ printf("%lld ",ans.second); } else{ printf("N "); } } return 0; }