• AGC31E Snuke the Phantom Thief


    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
    ",ans);
    	return 0;
    }
    

    好久不写费用流都写不对了……

  • 相关阅读:
    算法导论图论图的表示 课后题答案
    MFC 如何添加快捷键
    全排序算法permutation分析与总结
    java k++ 和C/C++ k++的区别
    找回失去的快捷方式向导
    解开注册表中U盘禁止拷贝的限制
    锐捷多网卡解决方案 与当前环境冲突(Code 2)
    Delphi中的Access技巧集
    Delphi MessageBox对话框
    另一个博客,不知道好用不
  • 原文地址:https://www.cnblogs.com/autoint/p/12204074.html
Copyright © 2020-2023  润新知