[AGC031E] Snuke the Phantom Thief(网络流)

考虑枚举偷的珠宝的个数k,且假设它们按照坐标大小排好了序(x坐标排一次,y坐标排一次)。

那么可以将条件转化一下,

在珠宝按x坐标排好序时,
x坐标大于等于 a i a_i ai的最多取 b i b_i bi个可以转化为取的前 k − b i k-b_i kbi个珠宝的x坐标要小于 a i a_i ai
同理,x坐标小于等于 a i a_i ai的最多可以取 b i b_i bi个可以转化为取的后 k − b i k-b_i kbi个珠宝的x坐标要大于 a i a_i ai

y坐标同理。

那么这样的话,就可以计算出取的每个珠宝的x,y坐标取值范围。

接下来有两种处理方法。
法一:
在这里插入图片描述
法二:
在这里插入图片描述

/*妙!把 对区间内物品选择个数的限制 转为 选择一定数量物品时,对物品坐标范围的限制 
达到优化建图的效果*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int N=400;
const int M=15000;
const int inf=2147483647;
struct Edge{
	int u,v,f,nxt;ll w;
}edge[M<<1];
int head[N],cnt,s,t,inque[N],pre[N];ll dis[N];
queue<int> que;
int n,m;ll ans;
struct Jewelry{
	int x,y;ll val;
}p[85];
struct Quiery{
	char ch;int a,b;
}q[325];
void add(int u,int v,int f,ll w){
	edge[cnt].u=u;edge[cnt].v=v;edge[cnt].w=w;edge[cnt].f=f;edge[cnt].nxt=head[u];head[u]=cnt++;
	edge[cnt].u=v;edge[cnt].v=u;edge[cnt].w=-w;edge[cnt].f=0;edge[cnt].nxt=head[v];head[v]=cnt++;
}
bool spfa(){
	memset(dis,-63,sizeof(dis));
	memset(inque,0,sizeof(inque));
	memset(pre,-1,sizeof(pre));
	dis[s]=0;
	que.push(s);inque[s]=1;
	while(!que.empty()){
		int u=que.front();
		que.pop();inque[u]=0;
		for(int i=head[u];i!=-1;i=edge[i].nxt){
			int v=edge[i].v;
			if(edge[i].f>0&&dis[v]<dis[u]+edge[i].w){
				dis[v]=dis[u]+edge[i].w;
				pre[v]=i;
				if(!inque[v]){
					que.push(v);inque[v]=1;
				}
			}
		}	
	}
	if(pre[t]==-1) return 0;
	return 1;
}
ll EK(){
	int flow;
	ll ret=0;
	while(spfa()){
		flow=inf;
		int x=pre[t];
		while(x!=-1){
			flow=min(edge[x].f,flow);
			x=pre[edge[x].u];
		}
		x=pre[t];
		while(x!=-1){
			edge[x].f-=flow;
			edge[x^1].f+=flow;
			ret+=flow*edge[x].w;
			x=pre[edge[x].u];
		}
	}
	return ret;
}
int lx[85],rx[85],ly[85],ry[85];
void solve(int k){
	memset(head,-1,sizeof(head));cnt=0;
	memset(lx+1,0,k<<2),memset(ly+1,0,k<<2),memset(rx+1,0x7f,k<<2),memset(ry+1,0x7f,k<<2);
	for(int i=1;i<=m;i++){
		if(q[i].ch=='L') lx[q[i].b+1]=max(lx[q[i].b+1],q[i].a+1);
		if(q[i].ch=='R'&&k>q[i].b) rx[k-q[i].b]=min(rx[k-q[i].b],q[i].a-1);
		if(q[i].ch=='D') ly[q[i].b+1]=max(ly[q[i].b+1],q[i].a+1);
		if(q[i].ch=='U'&&k>q[i].b) ry[k-q[i].b]=min(ry[k-q[i].b],q[i].a-1);
	}
	for(int i=2;i<=k;i++) lx[i]=max(lx[i],lx[i-1]),ly[i]=max(ly[i],ly[i-1]);
	for(int i=k-1;i;i--) rx[i]=min(rx[i],rx[i+1]),ry[i]=min(ry[i],ry[i+1]);
	s=0;t=k+n+n+k+1;
	for(int i=1;i<=k;i++){
		add(s,i,1,0);
		for(int j=1;j<=n;j++){
			if(lx[i]<=p[j].x&&p[j].x<=rx[i]) add(i,k+j,1,0);
			if(ly[i]<=p[j].y&&p[j].y<=ry[i]) add(k+n+j,k+n+n+i,1,0);
		}
		add(k+n+n+i,t,1,0);
	}
	for(int j=1;j<=n;j++) 
		add(k+j,k+n+j,1,p[j].val);
	ans=max(ans,EK());
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d%lld",&p[i].x,&p[i].y,&p[i].val);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
		scanf("%s%d%d",&q[i].ch,&q[i].a,&q[i].b);
	for(int k=1;k<=n;k++) solve(k);
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值