Snuke the Phantom Thief
有 \(N\) 个珠宝,第 \(i\) 个位于 \((x_i, y_i)\),价值为 \(v_i\)。你可以选择一些珠宝,有若干限制,每个限制形如如下四种之一:
\(x ≤ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
\(x ≥ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
\(y ≤ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
\(y ≥ a_i\) 的珠宝只能选择不超过 \(b_i\) 个;
最大化选择的总价值。
\(1 ≤ N ≤ 80,1 ≤ x_i, y_i, a_i≤ 100\)。
题解
https://www.cnblogs.com/zhoushuyu/p/10548483.html
首先这数据范围看着就很费用流
先考虑一维怎么做。
一个很妙的转化是:限制横坐标 \(≤a_i\) 的珠宝里至多选 \(b_i\) 个,等价于选择的横坐标第 \(b_{i+1}\) 小的珠宝,其横坐标必须 \(>a_i\)。
如果是限制横坐标 \(≥a_i\) 的珠宝至多选 \(b_i\) 个,则可以先枚举选 \(s\) 个珠宝,然后限制就等价于选择的第 \(s-b_i\) 个珠宝其横坐标必须 \(<a_i\)。(前述只考虑 \(b_i < s\) 的限制,\(b_i ≥ s\) 的限制显然无效)
这样我们就可以得到 \(s\) 个二元组 \([l_j,r_j]\),分别表示第 \(j\) 个珠宝的横坐标的范围限制。注意这 \(s\) 个二元组的 \(l\) 和 \(r\) 应满足单调不降。
这样我们就得到了一个匹配的模型:二分图一侧有 \(s\) 个点,另一侧有 \(n\) 个点,满足范围限制的点之间连边,然后求一组最大权匹配即可。
至于二维的问题,可以直接把图拆成三份,即左侧 \(s\) 个点表示横坐标的限制,中间拆 \(2n\) 个点内部连权值的边表示珠宝,右侧另 \(s\) 个点表示纵坐标的限制。
#include<bits/stdc++.h> using namespace std; #define CO const #define IN inline typedef long long int64; template<class T> IN T read(){ T x=0,w=1;char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-') w=-w; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*w; } template<class T> IN T read(T&x){ return x=read<T>(); } CO int N=400; CO int64 inf=1e18; namespace flow{ int n,S,T; struct edge {int v,c;int64 w;int a;}; vector<edge> to[N]; int64 dis[N];int vis[N]; IN void init(int n){ flow::n=n,S=n-1,T=n; for(int i=1;i<=n;++i) to[i].clear(); } IN void link(int u,int v,int c,int64 w){ to[u].push_back({v,c,w}),to[v].push_back({u,0,-w}); to[u].back().a=to[v].size()-1,to[v].back().a=to[u].size()-1; } bool bfs(){ fill(dis+1,dis+n+1,-inf),dis[T]=0; deque<int> Q={T};vis[T]=1; while(Q.size()){ int u=Q.front(); Q.pop_front(),vis[u]=0; for(CO edge&e:to[u])if(to[e.v][e.a].c){ if(dis[e.v]<dis[u]-e.w){ // edit 1: -w dis[e.v]=dis[u]-e.w; if(vis[e.v]) continue; if(Q.size() and dis[e.v]>=dis[Q.front()]) Q.push_front(e.v); else Q.push_back(e.v); vis[e.v]=1; } } } return dis[S]>-inf; } int dfs(int u,int lim){ if(u==T) return lim; vis[u]=1; int rest=lim; for(edge&e:to[u])if(!vis[e.v] and e.c and dis[e.v]==dis[u]-e.w){ int delta=dfs(e.v,min(e.c,rest)); if(!delta) {dis[e.v]=-inf;continue;} rest-=delta,e.c-=delta,to[e.v][e.a].c+=delta; if(!rest) break; } vis[u]=0; return lim-rest; } int64 main(){ int64 ans=0; while(bfs()) ans+=dfs(S,1e9)*dis[S]; return ans; } } int n,X[N],Y[N];int64 V[N]; int m,O[N],A[N],B[N]; int L[N],R[N],D[N],U[N]; int64 solve(int s){ fill(L+1,L+s+1,0),fill(D+1,D+s+1,0); fill(R+1,R+s+1,233),fill(U+1,U+s+1,233); // edit 2: range of XY for(int i=1;i<=m;++i)if(B[i]<s){ if(O[i]=='L') L[B[i]+1]=A[i]+1; else if(O[i]=='R') R[s-B[i]]=A[i]-1; else if(O[i]=='D') D[B[i]+1]=A[i]+1; else U[s-B[i]]=A[i]-1; } for(int i=2;i<=s;++i){ L[i]=max(L[i],L[i-1]); D[i]=max(D[i],D[i-1]); } for(int i=s-1;i>=1;--i){ R[i]=min(R[i],R[i+1]); U[i]=min(U[i],U[i+1]); } flow::init(2*n+2*s+2); for(int i=1;i<=n;++i) flow::link(i,i+n,1,V[i]); for(int i=1;i<=s;++i){ flow::link(flow::S,i+2*n,1,0),flow::link(i+2*n+s,flow::T,1,0); for(int j=1;j<=n;++j){ if(L[i]<=X[j] and X[j]<=R[i]) flow::link(i+2*n,j,1,0); if(D[i]<=Y[j] and Y[j]<=U[i]) flow::link(j+n,i+2*n+s,1,0); } } return flow::main(); } int main(){ read(n); for(int i=1;i<=n;++i) read(X[i]),read(Y[i]),read(V[i]); read(m); for(int i=1;i<=m;++i){ char opt[2];scanf("%s",opt); O[i]=opt[0],read(A[i]),read(B[i]); } int64 ans=0; for(int i=1;i<=n;++i) ans=max(ans,solve(i)); printf("%lld\n",ans); return 0; }
好久不写费用流都写不对了……
来源:https://www.cnblogs.com/autoint/p/12204074.html