题面:
有两个集合,现在又n个点,第i个点在A集合花费Ai代价,在B集合花费Bi代价
然后又m个限制,每个限制是a,b,c,说a和b如果不在一个集合就会多花费c代价。
现在要让每个点属于一个集合,求最小代价
题解:
相当于把n个点划分为两个集合,我们设A为源点S,B为汇点T,对于每个点向S和T连权值大小的边
对于每个限制a,b,c,建双向边让a连b,b连a,大小都为c,求出图中的最小割,就是把n个点,划分为两个集合的最小代价
又因为最小割==最大流,跑DINIC即可了.
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define N 20010 #define M 800010 #define INF 100000000 using namespace std; int head[N],cur[N],lev[N],ecnt=1,S,T,n,m; queue <int> q; struct adj { int nxt,v,w; }e[2*M]; void add(int u,int v,int w) { e[++ecnt].v=v,e[ecnt].w=w,e[ecnt].nxt=head[u],head[u]=ecnt; e[++ecnt].v=u,e[ecnt].w=0,e[ecnt].nxt=head[v],head[v]=ecnt; } inline int bfs() { int u,v; for (int i=S;i<=T;i++) lev[i]=-1,cur[i]=head[i]; q.push(S),lev[S]=0; while (!q.empty()) { u=q.front(); for (int i=head[u];i;i=e[i].nxt) { if (e[i].w>0 && lev[v=e[i].v]==-1) lev[v]=lev[u]+1,q.push(v); } q.pop(); } return lev[T]!=-1; } inline int Dinic(const int &u,const int &flow) { if (u==T) return flow; int res=0,v,delta; for (int &i=cur[u];i;i=e[i].nxt) { if (e[i].w>0 && lev[u]<lev[v=e[i].v]) { delta=Dinic(v,min(e[i].w,flow-res)); if (delta) { e[i].w-=delta;e[i^1].w+=delta; res+=delta;if (res==flow) break; } } } if (res!=flow) lev[u]=-1; return res; } inline int Maxflow() { int ans=0; while (bfs()) ans+=Dinic(S,INF); return ans; } inline void init() { memset(head,0,sizeof(head)); ecnt=1; } int main() { scanf("%d%d",&n,&m); S=0,T=n+1; for (int i=1,a,b;i<=n;i++) scanf("%d%d",&a,&b),add(S,i,a),add(i,T,b); for (int i=1,u,v,w;i<=m;i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w); printf("%d",Maxflow()); return 0; }