找出若干个环覆盖所有的点,使得总的花费最小
因为每个点只能经过一次,所以很快就可想到拆点求最小费用流
建图:
S->i 费用为0 流量为1
i+n->T同上
若有边u->v
u->v+n 费用为边权,容量为1
最后套套模板求一次最小费用流,如果流量等于n,表示每个点都遍历了一次,输出最小费用即可
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int n_max=505;
const int m_max=10005;
const int inf=1<<30;
int min_cost,max_flow;
int head[n_max];
struct edge
{
int u,v,cap,cost,last;
void add(int pu,int pv,int pcap,int pcost,int plast)
{
u=pu;
v=pv;
cap=pcap;
cost=pcost;
last=plast;
}
}e[m_max<<2];
int ne;
void init()
{
ne=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
e[ne].add(u,v,cap,cost,head[u]);
head[u]=ne++;
e[ne].add(v,u,0,-cost,head[v]);
head[v]=ne++;
}
void min_cost_max_flow(int s,int t)
{
int d[n_max],p[n_max];
bool inq[n_max];
int id=0;
min_cost=max_flow=0;
while(true)
{
int i,u,v;
for(i=0;i<n_max;i++)
{
d[i]=inf;
}
d[s]=0;
memset(inq,0,sizeof(inq));
memset(p,-1,sizeof(p));
queue<int> q;
q.push(s);
inq[s]=true;
while(!q.empty())
{
u=q.front();
q.pop();
inq[u]=false;
for(i=head[u];i!=-1;i=e[i].last)
{
v=e[i].v;
if(e[i].cap&&d[v]>d[u]+e[i].cost)
{
d[v]=d[u]+e[i].cost;
p[v]=i;
if(!inq[v])
{
q.push(v);
inq[v]=true;
}
}
}
}
if(d[t]==inf)
{
break;
}
int nmin=inf;
for(u=p[t];u!=-1;u=p[e[u].u])
{
nmin=min(nmin,e[u].cap);
}
for(u=p[t];u!=-1;u=p[e[u].u])
{
e[u].cap -= nmin;
e[u^1].cap += nmin;
}
max_flow+=nmin;
min_cost+=d[t]*nmin;
}
}
int main()
{
int n,m,s,t;
while(~scanf("%d%d",&n,&m))
{
s=0;
t=n+n+1;
init();
int i,u,v,w;
for(i=1;i<=n;i++)
{
addedge(s,i,1,0);
addedge(n+i,t,1,0);
}
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,n+v,1,w);
}
min_cost_max_flow(s,t);
if(max_flow!=n)
{
printf("-1\n");
}
else
{
printf("%d\n",min_cost);
}
}
}