P5222 Game 题解
题目大意
一个\(N\times M\)的棋盘中有\(T\)个障碍点,棋子不能放在障碍点上,刚开始棋子要放在第一列上,有两种操作
-
将所有棋子都向右移一格,不需要代价
-
将其中一个棋子向上或者向下平移一格,代价是\(1\)
询问\(Q\)次,每次询问给出\(k_i\)的代价,最多可以将多少棋子从第一列移到最后一列
问题求解
从代价去求棋子数很难,我们可以转化一下,已知棋子数求代价,用\(ans_i\)表示\(i\)个棋子从第一列移到最后一列的最小代价,最后询问二分求解即可
然后来分析如何求得\(ans_i\)显然我们可以发现,障碍点的个数比较小,一段没有障碍点的棋盘我们可以将棋子直接移到有障碍点出,所以可以离散化掉,相当于将一段没有棋子的棋盘压缩成一列,将\(M\)变小,方便于求解,这个是一个非常重要的贪心
现在,\(M\)的大小已经和\(T\)同级了,对于每个棋子到底怎么移动我们很难分析,但是可以控制开始棋子数和结束的棋子数,所以考虑用费用流来解决这道题
对于一个不是障碍点的点,对不是障碍点的点建边
-
向右边建一条流量为\(1\)费用为\(0\)的点,流量保证了每个点只能放一颗棋子
-
向上下分别建一条流量为\(INF\)费用为\(1\)的点,以为上下是可以随便移动的,代价为移动的距离
和很多费用流的题一样,将第一列的所有节点向一个超级原点建边,将最后一列的所有节点向超级汇点建边,流量为\(1\)费用为\(0\),原因同上
我们刷费用流时,每一次增广相当于多放一颗棋子,增广\(i\)次的代价就是\(ans_i\),当这个图不能再增广时说明已经是棋子最多数了
以上就是本篇题解的内容,但是还没有结束
\(yzxoi\)的题解中提到了一个缩点的想法,将一段没有障碍点的点缩成一个点,将边的数量降到了\(NT\),是一个非常非常不错的想法,在随机图上能跑出不错的效果
但是\(wpy\)苦思冥想了一下午,发现了一种\(hack\)的情况,如下图
其中黑色的是障碍点,按照缩点算法,我们将红色的点缩成了一个点,所以这个想法在不随机的情况下还是有些问题的,不过能想到还是%%%
代码实现
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2e5+5,maxe=4e6+5;
int N,M,K,q,Ans[55],st,st_st,ed,mp[55][4005],lst;
int lnk[maxn],son[maxe],nxt[maxe],w[maxe],c[maxe],cnt=1,Q[maxn];
int dis[maxn],vis[maxn],mincost;
struct AS{
int x,y;
bool operator <(const AS B)const {return y<B.y||(y==B.y&&x<B.x);}
}bob[2005];
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void add_e(int x,int y,int z,int cost){
son[++cnt]=y;w[cnt]=z;c[cnt]=cost;nxt[cnt]=lnk[x];lnk[x]=cnt;
}
void add_edge(int x,int y,int z,int cost){
add_e(x,y,z,cost);add_e(y,x,0,-cost);
}
int calc(int x,int y){
return M*(x-1)+y;
}
int spfa(){
int hed=0,til=1;
memset(dis,INF,sizeof dis);
memset(vis,0,sizeof vis);
dis[ed]=0;Q[til]=ed;
while(hed!=til){
hed=(hed+1)%maxn;
int cur=Q[hed];vis[cur]=0;
for(int j=lnk[cur];j;j=nxt[j]){
if(w[j^1]>0&&dis[son[j]]>dis[cur]-c[j]){
dis[son[j]]=dis[cur]-c[j];
if(!vis[son[j]]){
vis[son[j]]=1;Q[til=(til+1)%maxn]=son[j];
int nxt=(hed+1)%maxn;if(dis[Q[til]]<dis[Q[nxt]]) swap(Q[til],Q[nxt]);
}
}
}
}
return dis[st]!=INF;
}
int DFS(int x,int delta){
vis[x]=1;
if(x==ed||!delta)return delta;
int flow=0,tmp;
for(int j=lnk[x];j;j=nxt[j]){
if(!vis[son[j]]&&w[j]>0&&dis[son[j]]==dis[x]-c[j]&&(tmp=DFS(son[j],min(delta-flow,w[j])))){
mincost+=(c[j]*tmp);
w[j]-=tmp;w[j^1]+=tmp;flow+=tmp;
}
if(flow==delta)return flow;
}
return flow;
}
int Dinic(){
int maxflow=0;
if(spfa()){
int temp=DFS(st,INF);
maxflow+=temp;
}
return maxflow;
}
int main(){
freopen("lord.in","r",stdin);
freopen("lord.out","w",stdout);
N=read();M=read();K=read();q=read();
for(int i=1;i<=K;i++){bob[i].x=read(),bob[i].y=read();}
M=1;sort(bob+1,bob+1+K);
for(int i=1;i<=K;i++){
if(i!=1&&bob[i].y!=lst){
if(bob[i].y==lst+1)M++;
else M+=2;
}
lst=bob[i].y;bob[i].y=M;
mp[bob[i].x][bob[i].y]=1;
}
st=N*M+1;ed=st+1;st_st=ed+1;
for(int i=1;i<=N;i++)if(!mp[i][1]){
add_edge(st,calc(i,1),1,0);
}
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++){
if(mp[i][j])continue;
if(!mp[i][j+1]&&j<M)add_edge(calc(i,j),calc(i,j+1),1,0);
if(!mp[i-1][j]&&i>1)add_edge(calc(i,j),calc(i-1,j),INF,1);
if(!mp[i+1][j]&&i<N)add_edge(calc(i,j),calc(i+1,j),INF,1);
}
for(int i=1;i<=N;i++)if(!mp[i][M]){
add_edge(calc(i,M),ed,1,0);
}
for(int i=1;i<=N;i++){
int ans=Dinic();
if(ans)Ans[i]=mincost;else N=i-1;
}
for(int i=1;i<=q;i++){
int x=read();
printf("%d\n",upper_bound(Ans+1,Ans+1+N,x)-Ans-1);
}
return 0;
}
原文地址:https://www.cnblogs.com/martian148/p/15182557.html
- Android手机开机自动启动
- spring Boot 配置文件详解
- Fragment回退栈及弹出方法
- RepeatMasker安装和使用
- 漏洞追踪:如何检查并修复GHOST(幽灵)漏洞
- 锁的实现原理解锁的实现加锁的实现
- 幽灵漏洞(GHOST)影响大量Linux操作系统及其发行版(更新修复方案)
- openfire中mysql的前期设置
- android中最先被执行的activity
- Spring Boot微服务架构入门
- 测序文章数据上传找哪里
- Volatile实现原理实现原子性happens-before关系从happends-before规则分析可见性编译器层面实现可见性处理器层面实现可见性
- java中的toString方法
- 简单可视化-送你一双发现美的眼睛
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python五十七课——正则表达式(边界字符)
- python五十七课——正则表达式(多个字符)
- python五十八课——正则表达式(分组)
- python五十八课——正则表达式(替换)
- 配置Tomcat使用https协议(单向认证)
- python五十八课——正则表达式(切割)
- python五十九课——正则表达式的拓展内容
- 配置SVN的hooks功能自动更新代码
- python六十课——高阶函数之map
- python六十一课——高阶函数之reduce
- python六十二课——高阶函数之filter
- Nginx优化配置详解
- python六十三课——高阶函数之sorted
- python六十四课——高阶函数练习题(一)
- python六十四课——高阶函数练习题(二)