【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)
【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)
题面
题解
一道二合一的题目
考虑第一问。
先考虑如何计算六个方向上的第一个点。
左右上很好考虑,只需要按照\(x\)或者\(y\)轴排序就行了。
对于\(45\)度的斜角,两点一定在同一条直线上。
这条直线是\(x+y=b\)或\(x-y=b\)
所以按照\(x+y\)和\(x-y\)的值分类考虑,再按照顺序在\(x\)轴扫一遍就可以找到了。
考虑如何计算第一问的答案,我们发现\(y\)轴是单调不降的。
所以可以以\(y\)来划分截断,按照\(y\)轴从上往下进行转移。
设\(f[i]\)表示\(i\)的答案,对于所有\(y\)值相同的点放在一起解决。
每次转移的时候,先将当前\(y\)相同的所有点通过左上右上和上三个方向进行转移。
然后再考虑左右的转移。因为左右转移是可以先向一个方向绕过去再走回来的,
所以考虑转移的时候需要额外注意一下。
所以再诶外设一个\(g\),\(f[i]\)表示从\(i\)出发不在同层内转移的最优答案。
\(g[i]\)表示从\(i\)出发,先转移到同层的某个点的最优答案。
第二问
先把所有可以存在于一个最优方案下的点连边。
因为每条边都至少被覆盖一次,发现这是一个上下界网络流问题。
因为保证有解,直接解决就好了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
using namespace std;
#define ll long long
#define RG register
#define MAX 50505
#define inf 1e6
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int n;
struct Node{int x,y,id;}p[MAX];
bool cmpx(Node a,Node b){if(a.x!=b.x)return a.x<b.x;return a.y<b.y;}
bool cmpy(Node a,Node b){if(a.y!=b.y)return a.y<b.y;return a.x<b.x;}
bool cmpn(Node a,Node b){return a.id<b.id;}
int nt[MAX][5];
map<int,int> M;
int f[MAX],g1[MAX],g2[MAX],g[MAX],zy[MAX],a[MAX];
namespace Task1
{
void Getdirt()
{
sort(&p[1],&p[n+1],cmpx);
for(int i=1;i<n;++i)if(p[i].x==p[i+1].x)nt[p[i].id][2]=p[i+1].id;
for(int i=1;i<=n;++i)if(p[i].x==0){nt[0][2]=p[i].id;break;}//up
M.clear();
for(int i=1;i<=n;++i)//left-up
{
int s=p[i].x+p[i].y;
if(M[s])nt[p[i].id][3]=M[s];
M[s]=p[i].id;
if(s==0)nt[0][3]=p[i].id;
}
M.clear();
for(int i=n;i>=1;--i)//right-up
{
int s=p[i].y-p[i].x;
if(M[s])nt[p[i].id][4]=M[s];
M[s]=p[i].id;
if(s==0)nt[0][4]=p[i].id;
}
}
int st[MAX],top;
void Output(int x,int ans)
{
if(!ans)return;
if(f[x]==ans){printf("%d ",x);Output(zy[x],ans-1);return;}
int pos=a[x];while(pos>1&&p[pos-1].y==p[a[x]].y)--pos;
top=0;while(p[pos].y==p[a[x]].y&&pos<=n)st[++top]=p[pos++].id;
for(int i=1;i<=top;++i)if(st[i]==x){pos=i;break;}
for(int i=1;i<pos;++i)
if(f[st[i]]+top-i==g[x])
{
for(int j=pos;j<=top;++j)printf("%d ",st[j]);
for(int j=pos-1;j>i;--j)printf("%d ",st[j]);
Output(st[i],f[st[i]]);
return;
}
for(int i=top;i>pos;--i)
if(f[st[i]]+i-1==g[x])
{
for(int j=pos;j>=1;--j)printf("%d ",st[j]);
for(int j=pos+1;j<i;++j)printf("%d ",st[j]);
Output(st[i],f[st[i]]);
return;
}
}
void Solve()
{
Getdirt();sort(&p[1],&p[n+1],cmpy);
for(int i=1;i<=n;++i)a[p[i].id]=i;
for(int i=n,pos;i;i=pos)
{
top=0;pos=i;
while(pos&&p[pos].y==p[i].y)st[++top]=p[pos--].id;
reverse(&st[1],&st[top+1]);
for(int j=1;j<=top;++j)
for(int k=2;k<=4;++k)
{
int u=st[j];
if(f[u]<g[nt[u][k]]+1)
f[u]=g[nt[u][k]]+1,zy[u]=nt[u][k];
}
for(int j=1;j<=top;++j)g[st[j]]=f[st[j]];
for(int j=2,nw=1;j<=top;++j)
{
g[st[j]]=max(g[st[j]],f[st[nw]]+top-nw);
if(f[st[nw]]-nw<f[st[j]]-j)nw=j;
}
for(int j=top-1,nw=top;j>=1;--j)
{
g[st[j]]=max(g[st[j]],f[st[nw]]+nw-1);
if(f[st[nw]]+nw<f[st[j]]+j)nw=j;
}
}
for(int k=2;k<=4;++k)
if(f[0]<=g[nt[0][k]])f[0]=g[nt[0][k]],zy[0]=nt[0][k];
printf("%d\n",f[0]);
Output(zy[0],f[0]);puts("");
}
}
namespace Task2
{
struct Line{int v,next,w;}e[MAX<<5];
int h[MAX],cnt=2,M[MAX],SS,TT,S,T;
inline void Add(int u,int v,int w)
{
e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
}
set<pair<int,int> > SSS;
bool vis[MAX];
void Link(int x,int ans,bool fl)
{
if(!ans)return;
if(!fl)if(SSS.count(make_pair(x,ans)))return;
if(!fl)SSS.insert(make_pair(x,ans));
if(f[x]==ans&&!vis[x])
{
vis[x]=true;
for(int k=2;k<=4;++k)
if(f[x]==g[nt[x][k]]+1)
{
Add(x,nt[x][k],inf);
M[x]-=1;M[nt[x][k]]+=1;
Link(nt[x][k],ans-1,1);
}
}
if(!fl)return;
vector<int> st;int top=0;st.clear();st.push_back(0);
int pos=a[x];while(pos>1&&p[pos-1].y==p[a[x]].y)--pos;
top=0;while(p[pos].y==p[a[x]].y&&pos<=n)st.push_back(p[pos++].id),++top;
for(int i=1;i<=top;++i)if(st[i]==x){pos=i;break;}
for(int i=1;i<pos;++i)if(f[st[i]]+top-i==g[x])Link(st[i],f[st[i]],0);
for(int i=top;i>pos;--i)if(f[st[i]]+i-1==g[x])Link(st[i],f[st[i]],0);
}
int level[MAX],cur[MAX];
bool bfs(int S,int T)
{
memset(level,0,sizeof(level));level[S]=1;
queue<int> Q;Q.push(S);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=h[u];i;i=e[i].next)
if(e[i].w&&!level[e[i].v])
level[e[i].v]=level[u]+1,Q.push(e[i].v);
}
return level[T];
}
int dfs(int u,int T,int flow)
{
if(u==T||!flow)return flow;
int ret=0;
for(int &i=cur[u];i;i=e[i].next)
{
int v=e[i].v,d;
if(e[i].w&&level[v]==level[u]+1)
{
d=dfs(v,T,min(flow,e[i].w));
ret+=d;flow-=d;
e[i].w-=d;e[i^1].w+=d;
if(!flow)break;
}
}
return ret;
}
int Dinic(int S,int T)
{
int ret=0;
while(bfs(S,T))
{
memcpy(cur,h,sizeof(cur));
ret+=dfs(S,T,inf);
}
return ret;
}
int ans,rb;
void Solve()
{
S=n+1;T=n+2;SS=n+3;TT=n+4;
f[0]+=1;g[0]=inf;Link(0,f[0],0);
for(int i=0;i<=n;++i)Add(i,T,inf);
for(int i=0;i<=n;++i)Add(S,i,inf);
for(int i=0;i<=n;++i)
if(M[i]>0)Add(SS,i,M[i]);
else Add(i,TT,-M[i]);
Add(T,S,inf);rb=cnt-1;
Dinic(SS,TT);
h[T]=e[h[T]].next;h[S]=e[h[S]].next;
ans-=Dinic(T,S);ans+=e[rb].w;
printf("%d\n",ans);
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),p[i].id=i;
Task1::Solve();Task2::Solve();
return 0;
}
【BZOJ4200】【NOI2015】小园丁与老司机(动态规划,网络流)的更多相关文章
- [BZOJ4200][Noi2015]小园丁与老司机
4200: [Noi2015]小园丁与老司机 Time Limit: 20 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 106 Solved ...
- BZOJ4200 NOI2015小园丁与老司机(动态规划+上下界网络流)
一看上去就是一个二合一的题.那么先解决第一部分求最优路线(及所有可能在最优路线上的线段). 由于不能往下走,可以以y坐标作为阶段.对于y坐标不同的点,我们将可以直接到达的两点连边,显然这样的边的个数是 ...
- UOJ#132&bzoj4200[Noi2015]小园丁与老司机
看,这是一个传送门 Part A 把坐标离散化,按照纵坐标为第一关键字,横坐标为第二关键字排序 以$f_i$记录来到$i$这个点最多经过点数,那么答案显而易见就是$f_i$加上该层点数 转移的话就是分 ...
- bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)
传送门 这该死的码农题…… 题解在这儿->这里 //minamoto #include<iostream> #include<cstdio> #include<cs ...
- [UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机
[UOJ#132][BZOJ4200][luogu_P2304][NOI2015]小园丁与老司机 试题描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 \(n\) 棵许愿 ...
- 【BZOJ4200】[Noi2015]小园丁与老司机 DP+最小流
[BZOJ2839][Noi2015]小园丁与老司机 Description 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2, ...
- luogu P2304 [NOI2015]小园丁与老司机 dp 上下界网络流
LINK:小园丁与老司机 苦心人 天不负 卧薪尝胆 三千越甲可吞吴 AC的刹那 真的是泪目啊 很久以前就写了 当时记得特别清楚 写到肚子疼.. 调到胳膊疼.. ex到根不不想看的程度. 当时wa了 一 ...
- uoj132/BZOJ4200/洛谷P2304 [Noi2015]小园丁与老司机 【dp + 带上下界网络流】
题目链接 uoj132 题解 真是一道大码题,,,肝了一个上午 老司机的部分是一个\(dp\),观察点是按\(y\)分层的,而且按每层点的上限来看可以使用\(O(nd)\)的\(dp\),其中\(d\ ...
- BZOJ4200 & 洛谷2304 & UOJ132:[NOI2015]小园丁与老司机——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4200 https://www.luogu.org/problemnew/show/P2304 ht ...
- 【bzoj4200】[Noi2015]小园丁与老司机 STL-map+dp+有上下界最小流
题目描述 小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面.田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤ ...
随机推荐
- 两个字段联合约束(mysql)
联合约束:ALTER TABLE `lywl_provider_package` ADD unique(providerId,packCode) 给一个表建唯一约束
- Struts 2(七):国际化
基于Struts 2的Web应用国际化开发非常简单,其中Struts 2的国际化包括如下几部分:校验提示信息国际化.类型转换提示信息国际化.Action信息国际化以及JSP页面国际化. 第一节 JSP ...
- javaweb(三十三)——使用JDBC对数据库进行CRUD
一.statement对象介绍 Jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可. Statement对象的exe ...
- kallsyms , addr to symbol
#!/usr/bin/env python # addr2sym.py - resolve addresses to symbols, using a map file # Reads a log f ...
- Qt 利用XML文档,写一个程序集合 一
接到领导需求安排,说公司未来的硬件设备会越来越多,与每个设备对应的设备检测和设置程序也会增多.导致软甲太多,不好掌控.所以希望做一个完整的软件,但是呢,每个子程序还得独立,应为每个用户购买的设备不是一 ...
- CentOS7.2最小化安装后系统优化
系统初始化技术的演变 1.sysvinit技术 (1)Linux系统的第一个进程(pid=1)为init: Linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 b ...
- gopherjs
An example implementation of a GopherJS client and a Go server using the Improbable gRPC-Web impleme ...
- CSS Grid布局指南
简介 CSS Grid布局 (又名"网格"),是一个基于二维网格布局的系统,主要目的是改变我们基于网格设计的用户接口方式.如我们所知,CSS 总是用于网页的样式设置,但它并没有起到 ...
- scrum立会报告+燃尽图(第三周第二次)
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284 项目地址:https://coding.net/u/wuyy694 ...
- 欢迎来怼——第四次Scrum会议
一.小组信息 队名:欢迎来怼小组成员队长:田继平成员:李圆圆,葛美义,王伟东,姜珊,邵朔,冉华小组照片 二.开会信息 时间:2017/10/16 17:15~17:40,总计25min.地点:东北师范 ...