题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=1927
题解:
显然是个DAG
建图和有向图最小路径覆盖的建图有些相似。
都是拆点为 u u'分别表示出点和入点。
也都要保证每个点最多有一个出度和一个入度。
但因为带权,要求最小花费,切要满足每个点都去一次,既要满足流量,所以采用最小费用最大流。
S -> u :(1,0) 因为每个点都会到达,所以都可以有一个出度(可以从改点流出一个流量)。
S -> u':(1,a[u]) 传送到达 u点,提供一个入度方式
u -> v':(1,w) u的出度对应 v的入度
u'-> T :(1,0) 流向汇点,表示改点到达过。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define MAXN 2000 #define MAXM 50000 #define INF 0x3f3f3f3f3f3f3f3f #define ll long long using namespace std; struct Edge{ ll to[MAXM],cap[MAXM],cot[MAXM],nxt[MAXM],head[MAXN],ent; void Init(){ent=2;} void Adde(ll u,ll v,ll w,ll c){ to[ent]=v; cap[ent]=w; cot[ent]=c; nxt[ent]=head[u]; head[u]=ent++; to[ent]=u; cap[ent]=0; cot[ent]=-c;nxt[ent]=head[v]; head[v]=ent++; } ll Next(ll i,bool type){ return type?head[i]:nxt[i]; } }E; bool vis[MAXN]; ll cur[MAXN],dis[MAXN]; ll N,M,S,T; ll idx(ll i,ll k){ return i+k*N; } bool spfa(){ static bool inq[MAXN]; memset(inq,0,sizeof(inq)); memset(dis,0x3f,sizeof(dis)); ll u,v; queue<ll> q; dis[T]=0; inq[T]=1; q.push(T); while(!q.empty()){ u=q.front(); q.pop(); inq[u]=0; for(ll i=E.Next(u,1);i;i=E.Next(i,0)){ v=E.to[i]; if(!E.cap[i^1]||dis[v]<=dis[u]+E.cot[i^1]) continue; dis[v]=dis[u]+E.cot[i^1]; if(!inq[v]) q.push(v),inq[v]=1; } } return dis[S]<INF; } ll dfs(ll u,ll reflow){ if(u==T||!reflow) return reflow; ll flowout=0,f,v; vis[u]=1; for(ll &i=cur[u];i;i=E.Next(i,0)){ v=E.to[i]; if(vis[v]||dis[v]+E.cot[i]!=dis[u]) continue; f=dfs(v,min(reflow,E.cap[i])); flowout+=f; E.cap[i^1]+=f; reflow-=f; E.cap[i]-=f; if(!reflow) break; } if(!flowout) dis[u]=INF; return flowout; } ll Dinic(){ ll cost=0; while(spfa()){ memcpy(cur,E.head,sizeof(E.head)); memset(vis,0,sizeof(vis)); cost+=dfs(S,INF)*dis[S]; } return cost; } int main()//开long long { E.Init(); scanf("%lld%lld",&N,&M); S=N*2+1; T=N*2+2; for(ll i=1,w;i<=N;i++){//传送到达 i点。 scanf("%lld",&w); E.Adde(S,idx(i,1),1,w); } for(ll i=1,u,v,w;i<=M;i++){ scanf("%lld%lld%lld",&u,&v,&w); if(u>v) swap(u,v); E.Adde(idx(u,0),idx(v,1),1,w);//u的出度对应 v的入度。 } for(ll i=1;i<=N;i++){ E.Adde(S,idx(i,0),1,0);//因为每个点都会到达,所以都可以提供一个出度。 E.Adde(idx(i,1),T,1,0);//流向汇点,表示改点到达。 } ll cost=Dinic(); printf("%lld",cost); return 0; }