「题解」agc031_e Snuke the Phantom Thief
本文将同步发布于:
题目
题目链接:洛谷 AT4695、AtCoder 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的更多相关文章
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- 「题解」JOIOI 王国
「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...
- 「题解」:[loj2763][JOI2013]现代豪宅
问题 A: 现代豪宅 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...
- 「题解」:$Six$
问题 A: Six 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...
- 「题解」:$Smooth$
问题 A: Smooth 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...
- 「题解」:Kill
问题 A: Kill 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 80%算法 赛时并没有想到正解,而是选择了另一种正确性较对的贪心验证. 对于每一个怪,我们定义它的 ...
- 「题解」:y
问题 B: y 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 考虑双向搜索. 定义$cal_{i,j,k}$表示当前已经搜索状态中是否存在长度为i,终点为j,搜索过边 ...
- 「题解」:x
问题 A: x 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...
随机推荐
- [CTF]unicode编码
[CTF]unicode编码 --------------------- 作者:adversity` 来源:CSDN 原文:https://blog.csdn.net/qq_40836553/a ...
- Day009 冒泡排序
冒泡排序 冒泡排序无疑是最为出名的排序算法之一,总共有八大排序! 冒泡排序的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人人尽皆知. 我们看到嵌套循环,应该立马就可以得出这个算法的 ...
- 3 Java概述
java三大版本 javase:标准版(桌面程序,控制台开发) javame:嵌入式开发(手机,家电)目前陨落 javaee:企业级开发(web端..) JDK和JRE 定义 JDK是开发工具包 Jr ...
- 【maven】You may use+to add a project ro to let the plugin find all pom.xml files...
错误显示 解决方法 点击pom.xml,再Add as Maven Project 如果还不能解决,点击idea的log 复制报错(技巧:可以先将idea.log删除,比较好定位) Caused by ...
- Error querying database. Cause: java.lang.IllegalArgumentException:Failed to decrypt.(错误笔记)
java.lang.IllegalArgumentException:Failed to decrypt 从错误可以看出,解密失败. 原因是你在数据库连接配置的地方,设置了加密.即: config.d ...
- 你管这破玩意叫CPU?
每次回家开灯时你有没有想过,用你按的开关实际上能打造出 复杂的 CPU来,只不过需要的数量可能比较多,也就几十亿个吧. 伟大的发明 过去200年人类最重要的发明是什么?蒸汽机?电灯?火箭?这些可能都不 ...
- Codeforces Round #694 (Div. 2)
A. Strange Partition 题意:就是求最小和最大的bi/x向上取整的和. 思路:见题解:https://blog.csdn.net/qq_45900709/article/detai ...
- [刷题] PTA 7-61 找最长的字符串
程序: 1 #include<stdio.h> 2 #include<string.h> 3 #define N 81 4 5 int main() { 6 char ch[N ...
- 【海通国际】Joe Lowry(Mr. Lithium)谈全球电池原材料供应危机
[海通国际]Joe Lowry(Mr. Lithium)谈全球电池原材料供应危机 环球锂业公司(Global Lithium)总裁Joe Lowry日前接受了欧洲锰业Euro Manganese的邀请 ...
- 置的测试文件的大小一定要大过你的内存(最佳为内存的两倍大小),不然linux会给你的读写的内容进行缓存,会使数值非常不准确。
使用iozone测试磁盘性能 IOzone是一个文件系统测试基准工具.可以测试不同的操作系统中文件系统的读写性能.可以通过 write, re-write, read, re-read, rand ...