题意:给你有向边和点,每一个点只能且必须在一个环里面,问你所有环的权值和最小。
想法:二分匹配,每一个点拆成出入点a,a',出点为一组,入点为一组,现在然后就是可连的点连一条边二分匹配,求匹配之后的权值最小。用费用流解决
1.虚拟source和sink点
2.source向每一个a点连一条容量为1,费用为0的边
3.a->a'连一条容量为1,费用为题中所给的边
4.a'向每一个sink点连一条容量为1,费用为0的边
费用流就是卡时间我的运行时间是951ms
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define inf 0x7fffffff using namespace std; const int nodes=1050; const int edges=200100; int n,m,totfee,s,t,map[200+5][200+5]; struct node { int u,v,next; int fee,flow; }e[edges]; int head[nodes],cur[nodes],cnt; class Dinic { public: int spfa() { queue<int>q; while(!q.empty()) q.pop(); for(int i=s;i<=t;i++) { dis[i]=inf; } memset(vis,0,sizeof(vis)); memset(pe,-1,sizeof(pe)); vis[s]=1; dis[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i+1;i=e[i].next) { int v=e[i].v; if(dis[v]>dis[u]+e[i].fee&&e[i].flow>0) { dis[v]=dis[u]+e[i].fee; pe[v]=i; if(!vis[v]) { vis[v]=1; q.push(v); } } } } return dis[t]!=inf; } int Min(int a,int b) { if(a<b) return a; return b; } void result() { while(spfa()) { int minn=inf; for(int i=pe[t];i+1;i=pe[e[i].u]) { minn=Min(minn,e[i].flow); } for(int i=pe[t];i+1;i=pe[e[i].u]) { e[i].flow-=minn; e[i^1].flow+=minn; } totfee+=dis[t]*minn; } } private: int dis[nodes],vis[nodes],pe[nodes]; }dinic; void Init() { memset(head,-1,sizeof(head)); cnt=0; totfee=0; } void add(int a,int b,int flow,int fee) { e[cnt].u=a; e[cnt].v=b; e[cnt].flow=flow; e[cnt].fee=fee; e[cnt].next=head[a]; head[a]=cnt++; e[cnt].u=b; e[cnt].v=a; e[cnt].flow=0; e[cnt].fee=-fee; e[cnt].next=head[b]; head[b]=cnt++; } int main() { int test; scanf("%d",&test); while(test--) { Init(); scanf("%d%d",&n,&m); s=0;t=2*n+1; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j) map[i][j]=0; else map[i][j]=inf; } } for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(map[a][b]>c) { add(a,b+n,1,c); map[a][b]=c; } } for(int i=1;i<=n;i++) { add(i+n,t,1,0); add(s,i,1,0); } dinic.result(); printf("%d\n",totfee); } return 0; }