本文将同步发布于:

题目

题目链接:洛谷 AT4695AtCoder agc031_e

题意简述

在二维平面上,有 \(n\) 颗珠宝,第 \(i\) 颗珠宝在 \((x_i,y_i)\) 的位置,价值为 \(v_i\)。

现在有一个盗贼想要偷这些珠宝。

现在给出 \(m\) 个限制约束偷的珠宝,约束有以下四种:

  • 横坐标小于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗。
  • 横坐标大于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗。
  • 纵坐标小于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗。
  • 纵坐标大于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗。

现在问你在满足这些约束的条件下,盗贼偷的珠宝的最大价值和是多少。

题解

约束与转化

这个约束有点难办,似乎并没有可能对其进行动态规划,因此我们考虑额外添加信息。

我们添加什么信息呢?考虑到限制是有关珠宝数量的,我们决定添加一个 偷珠宝的总数 的信息。

设偷珠宝 \(k\) 颗,并且珠宝按照的横坐标排序被偷的顺序编号为 \(1\sim k\),那么前两种限制条件转化如下:

  • 横坐标小于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗:

    若一个珠宝的编号 \(\texttt{id}\in[b_i+1,k]\),那么一定有 \(x_{\texttt{id}}>a_i\)。
  • 横坐标大于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗:

    若一个珠宝的编号 \(\texttt{id}\in[1,k-b_i]\),那么一定有 \(x_{\texttt{id}}<a_i\)。

同理,我们可以得出珠宝按照的纵坐标排序被偷的顺序编号为 \(1\sim k\),那么后两种限制条件转化如下:

  • 纵坐标小于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗:

    若一个珠宝的编号 \(\texttt{id}\in[b_i+1,k]\),那么一定有 \(y_{\texttt{id}}>a_i\)。
  • 纵坐标大于等于 \(a_i\) 的珠宝最多偷 \(b_i\) 颗:

    若一个珠宝的编号 \(\texttt{id}\in[1,k-b_i]\),那么一定有 \(y_{\texttt{id}}<a_i\)。

因此,我们得出,一个珠宝想要在按横坐标排序为第 \(i\),纵坐标排序为第 \(j\) 时被偷,需要满足的坐标范围。

化腐朽为神奇的网络流

考虑上面的条件也不是很好动态规划,我们需要想到一种化腐朽为神奇的算法——网络流。

由于这个题目有多个互不相关的限制:

  • 珠宝不能同时被偷两次及以上;
  • 偷的珠宝价值要最大化;

我们考虑运用费用流建立网络流模型。

因为我们要限制横坐标,所以必须要有 \(k\) 个横坐标的限制,对应 \(s\to p_{1\sim k}\),流量为 \(1\),费用为 \(0\)。。

因为我们要限制纵坐标,所以必须要有 \(k\) 个纵坐标的限制,对应 \(q_{1\sim k}\to t\),流量为 \(1\),费用为 \(0\)。

因为我们要限制一个点不能被选择多次,所以我们需要拆点限流,对应 \(a_{1\sim n}\to b_{1\sim n}\),流量为 \(1\),费用为 \(v_i\)。

考虑到我们上面需要满足的限制,按照限制加边 \(p_i\to a_j\) 和 \(b_j\to q_i\) 即可,流量为 \(1\),费用为 \(0\)。

如果上面的语言有点抽象,我们不妨画图理解。

整个建图如上所示,点数为 \(\Theta(n^2)\),边数 \(\Theta(n^2)\)。

参考程序

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
static char buf[1<<21],*p1=buf,*p2=buf;
inline int read(void){
reg char ch=getchar();
reg int res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=10*res+(ch^'0'),ch=getchar();
return res;
} inline ll readll(void){
reg char ch=getchar();
reg ll res=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))res=10*res+(ch^'0'),ch=getchar();
return res;
} inline int max(reg int a,reg int b){
return a>b?a:b;
} inline int min(reg int a,reg int b){
return a<b?a:b;
} inline ll max(reg ll a,reg ll b){
return a>b?a:b;
} const int MAXN=80+5;
const int MAXM=320+5;
const int inf=0x3f3f3f3f; namespace Network{
const int MAXV=4*MAXN;
const int MAXE=(MAXN*MAXN*2+3*MAXN)*2;
const ll inf=0x3f3f3f3f3f3f3f3f;
int cnt,head[MAXV],to[MAXE],w[MAXE],Next[MAXE];
ll p[MAXE];
inline void Add_Edge(reg int u,reg int v,reg int len,reg ll val){
Next[++cnt]=head[u];
to[cnt]=v,w[cnt]=len,p[cnt]=val;
head[u]=cnt;
return;
}
inline void Add_Tube(reg int u,reg int v,reg int len,reg ll val){
Add_Edge(u,v,len,val);
Add_Edge(v,u,0,-val);
return;
}
bool inque[MAXV];
int cur[MAXV];
ll dis[MAXV];
queue<int> Q;
inline bool spfa(int s,reg int t){
fill(dis+s,dis+t+1,inf);
inque[s]=true,dis[s]=0,Q.push(s);
while(!Q.empty()){
reg int u=Q.front();
Q.pop();
inque[u]=false;
cur[u]=head[u];
for(reg int i=head[u];i;i=Next[i]){
int v=to[i];
if(dis[v]>dis[u]+p[i]&&w[i]){
dis[v]=dis[u]+p[i];
if(!inque[v]){
inque[v]=true;
Q.push(v);
}
}
}
}
return dis[t]!=inf;
}
bool vis[MAXV];
inline int dfs(reg int u,reg int t,reg ll lim){
if(u==t)
return lim;
vis[u]=true;
reg int flow=0;
for(reg int &i=cur[u];i;i=Next[i]){
reg int v=to[i];
if(dis[v]==dis[u]+p[i]&&w[i]&&!vis[v]){
reg int f=dfs(v,t,min(lim-flow,w[i]));
if(f){
flow+=f;
w[i]-=f,w[i^1]+=f;
if(flow==lim)
break;
}
}
}
vis[u]=false;
return flow;
}
inline pair<int,ll> dinic(reg int s,reg int t){
int res=0;
ll cost=0;
while(spfa(s,t)){
reg int flow=dfs(s,t,inf);
res+=flow,cost+=flow*dis[t];
}
return make_pair(res,cost);
}
inline void init(reg int s,reg int t){
cnt=1,fill(head+s,head+t+1,0);
return;
}
} struct Point{
int x,y;
ll v;
}; struct Limits{
char ch;
int a,b;
}; struct Interval{
int l,r;
inline Interval(reg int l=0,reg int r=0):l(l),r(r){
return;
}
inline bool in(reg int x)const{
return l<=x&&x<=r;
}
}; inline Interval cap(const Interval& a,const Interval& b){
return Interval(max(a.l,b.l),min(a.r,b.r));
} int n,m;
Point a[MAXN];
Limits b[MAXM]; int main(void){
n=read();
for(reg int i=1;i<=n;++i)
a[i].x=read(),a[i].y=read(),a[i].v=readll();
m=read();
for(reg int i=1;i<=m;++i){
do
b[i].ch=getchar();
while(!isalpha(b[i].ch));
b[i].a=read(),b[i].b=read();
}
reg ll ans=0;
for(reg int k=1;k<=n;++k){
static Interval invx[MAXN],invy[MAXN];
fill(invx+1,invx+k+1,Interval(-inf,inf));
fill(invy+1,invy+k+1,Interval(-inf,inf));
for(reg int i=1;i<=m;++i){
switch(b[i].ch){
case 'L':{
for(reg int j=b[i].b+1;j<=k;++j)
invx[j]=cap(invx[j],Interval(b[i].a+1,inf));
break;
}
case 'R':{
for(reg int j=1;j<=k-b[i].b;++j)
invx[j]=cap(invx[j],Interval(-inf,b[i].a-1));
break;
}
case 'D':{
for(reg int j=b[i].b+1;j<=k;++j)
invy[j]=cap(invy[j],Interval(b[i].a+1,inf));
break;
}
case 'U':{
for(reg int j=1;j<=k-b[i].b;++j)
invy[j]=cap(invy[j],Interval(-inf,b[i].a-1));
break;
}
}
}
reg int s=0,t=2*k+2*n+1;
Network::init(s,t);
for(reg int i=1;i<=k;++i){
Network::Add_Tube(s,i,1,0);
Network::Add_Tube(k+n+n+i,t,1,0);
}
for(reg int i=1;i<=n;++i)
Network::Add_Tube(k+i,k+n+i,1,-a[i].v);
for(reg int i=1;i<=k;++i)
for(reg int j=1;j<=n;++j){
if(invx[i].in(a[j].x))
Network::Add_Tube(i,k+j,1,0);
if(invy[i].in(a[j].y))
Network::Add_Tube(k+n+j,k+n+n+i,1,0);
}
pair<int,ll> res=Network::dinic(s,t);
if(res.first==k)
ans=max(ans,-res.second);
}
printf("%lld\n",ans);
return 0;
}

「题解」agc031_e Snuke the Phantom Thief的更多相关文章

  1. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  2. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  3. 「题解」JOIOI 王国

    「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...

  4. 「题解」:[loj2763][JOI2013]现代豪宅

    问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...

  5. 「题解」:$Six$

    问题 A: Six 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...

  6. 「题解」:$Smooth$

    问题 A: Smooth 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...

  7. 「题解」:Kill

    问题 A: Kill 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...

  8. 「题解」:y

    问题 B: y 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...

  9. 「题解」:x

    问题 A: x 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...

随机推荐

  1. 5.PHP与Web页面交互

    PHP与Web页面交互 PHP中提供了两种与Web页面交互的方法,一种是通过Web表单提交数据,另一种是通过URL参数传递. 表单提交用户名字和密码: <form name "form ...

  2. CVE-2019-11043 Nginx PHP 远程代码执行漏洞复现

    漏洞背景:来自Wallarm的安全研究员Andrew Danau在9月14-16号举办的Real World CTF中,意外的向服务器发送%0a(换行符)时,服务器返回异常信息.由此发现了这个0day ...

  3. Servlet三大域对象

    Servlet三大域对象的应用 request.session.application(ServletContext) ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器 ...

  4. CCNA 第二章 以太网回顾

    1:半双工和全双工 (1):半双工:类似于单车道: (2):全双工:类似是双向多车道: 2:思科三层模型 (1): (2):核心层.集散层(汇聚层).接入层各功能: 1:核心层:大量数据快速交换:不要 ...

  5. KMP算法以及优化(代码分析以及求解next数组和nextval数组)

    KMP算法以及优化(代码分析以及求解next数组和nextval数组) 来了,数据结构及算法的内容来了,这才是我们的专攻,前面写的都是开胃小菜,本篇文章,侧重考研408方向,所以保证了你只要看懂了,题 ...

  6. 技能Get·BOM头是什么?

    阅文时长 | 0.26分钟 字数统计 | 472.8字符 主要内容 | 1.引言&背景 2.BOM头是什么? 3.如何创建或取消BOM头? 4.如何判断文件是否包含BOM头? 5.声明与参考资 ...

  7. [云计算] OpenStack 发展史

    传统数据中心面临的问题 无法管理,资源利用率不高 资源分配不合理 初始成本高 发展阶段 IDC 托管/租用 VPS(虚拟专用主机/OpenVZ/XEN) 虚拟主机 云主机 虚拟化 服务器虚拟化 KVM ...

  8. 用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程

    GDB的那些奇淫技巧 evilpan 收录于 Security  2020-09-13  约 5433 字   预计阅读 11 分钟  709 次阅读  gdb也用了好几年了,虽然称不上骨灰级玩家,但 ...

  9. Jenkins远程代码执行漏洞

    于一个月前,进行服务器巡检时,发现服务器存在不明进程,并且以Jenkins用户身份来运行.当时进行了处理并修复了漏洞.在此补上修复过程 第一反应是Jenkins存在漏洞,于是Google Jenkin ...

  10. Docker的介绍和安装(1)

    一.虚拟化简介 1.虚拟化概念 计算机虚拟化(Computing Virtualization),一种资源管理技术,是指通过虚拟化技术将一台计算机的各种实体资源 , 如处理器 , 网络 , 内存及存储 ...