今天的代码怎么都这么难调啊!!!
T1:游戏
本
题
算
是
个
半
裸
的
S
T
L
模
板
题
本题算是个半裸的STL模板题
本题算是个半裸的STL模板题
题
意
:
有
w
列
h
行
,
对
于
a
≡
b
(
m
o
d
2
)
的
点
,
放
一
块
木
板
,
使
(
a
,
b
)
与
(
a
,
b
+
1
)
相
连
,
再
每
一
列
上
方
放
一
个
球
垂
直
下
落
,
遇
到
木
板
沿
着
木
板
往
相
邻
位
置
走
,
给
出
n
个
减
少
的
木
板
,
求
每
个
点
的
最
终
位
置
题意:有w列h行,对于a\equiv b\pmod{2}的点,放一块木板,使(a,b)与(a,b+1)相连,再每一列上方放一个球垂直下落,遇到木板沿着木板往相邻位置走,给出n个减少的木板,求每个点的最终位置
题意:有w列h行,对于a≡b(mod2)的点,放一块木板,使(a,b)与(a,b+1)相连,再每一列上方放一个球垂直下落,遇到木板沿着木板往相邻位置走,给出n个减少的木板,求每个点的最终位置
先
考
虑
没
有
减
少
木
板
的
情
况
,
观
察
第
一
列
发
现
,
由
于
相
邻
两
球
遇
到
同
一
块
木
板
时
位
置
交
换
,
所
以
可
以
对
每
一
列
分
别
考
虑
先考虑没有减少木板的情况,观察第一列发现,由于相邻两球遇到同一块木板时位置交换,所以可以对每一列分别考虑
先考虑没有减少木板的情况,观察第一列发现,由于相邻两球遇到同一块木板时位置交换,所以可以对每一列分别考虑
对
于
每
一
列
,
我
们
发
现
:
对于每一列,我们发现:
对于每一列,我们发现:
1.
若
为
奇
数
列
,
直
接
将
奇
偶
相
邻
交
换
1. 若为奇数列,直接将奇偶相邻交换
1.若为奇数列,直接将奇偶相邻交换
2.
若
为
偶
数
列
,
收
尾
不
变
,
中
间
的
相
邻
交
换
2. 若为偶数列,收尾不变,中间的相邻交换
2.若为偶数列,收尾不变,中间的相邻交换
考
虑
拆
掉
一
些
木
板
,
则
表
示
这
一
行
的
这
一
列
不
会
被
交
换
考虑拆掉一些木板,则表示这一行的这一列不会被交换
考虑拆掉一些木板,则表示这一行的这一列不会被交换
所
以
我
们
可
以
用
两
个
双
端
队
列
维
护
每
个
球
最
终
位
置
,
最
后
在
记
录
答
案
即
可
所以我们可以用两个双端队列维护每个球最终位置,最后在记录答案即可
所以我们可以用两个双端队列维护每个球最终位置,最后在记录答案即可
注
意
点
:
注意点:
注意点:
- deque和stack的运用,即下标不一定从1开始,应用迭代器iterator
- 木板为奇数列或者偶数列的时候,操作是不同的,因为如果是偶数列的话,与右边奇数列交换,此时奇数的序数(即在原序列中为第几个奇数)比偶数的序数大1,而如果是偶数的话,两个的序数是相等的
- 迭代器下标从0开始,end()应该是开区间
- 题目问的不是每个落点的最终球,而是每个球的最终落点,所以最后还需要计算一次
- 因为交换时为了方便,要再记录一个ans1,ans2(当然,大佬如果可以不用就忽略),ans1表示fir为奇数队列还是偶数队列,ans2表示sec为奇数队列还是偶数队列,然后在后面操作的时候要分两种情况讨论
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int h,w,n;
vector <int> ball[N];
deque <int> fir,sec;
int ans1=1,ans2=2,ans[N];
int main(){
scanf("%d%d%d",&h,&w,&n);
for(int i=1;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
ball[x].push_back(y);
}
for(int i=1;i<=w;i++){
if(i&1){
fir.push_back(i);
}
else{
sec.push_back(i);
}
}
for(int i=1;i<=h;i++){
if(i&1){//对奇偶行分别判断
swap(ans1,ans2);
vector <int>::iterator it;
for(it=ball[i].begin();it<ball[i].end();it++){//
int v=*it;
if(v%2==0){//对删掉的木板是奇数列还是偶数列讨论
int tmp=v/2;
if(ans1==1)//对奇偶队列产生的影响讨论
{
int tmpf=fir[tmp],tmps=sec[tmp-1];
fir[tmp]=tmps,sec[tmp-1]=tmpf;//
}
if(ans2==1)//对奇偶队列产生的影响讨论
{
int tmpf=fir[tmp-1],tmps=sec[tmp];
fir[tmp-1]=tmps,sec[tmp]=tmpf;//
}
}
if(v%2==1){//对删掉的木板是奇数列还是偶数列讨论
int tmp=v/2+1;
if(ans1==1)//对奇偶队列产生的影响讨论
{
int tmpf=fir[tmp-1],tmps=sec[tmp-1];
fir[tmp-1]=tmps,sec[tmp-1]=tmpf;//
}
if(ans2==1)//对奇偶队列产生的影响讨论
{
int tmpf=fir[tmp-1],tmps=sec[tmp-1];
fir[tmp-1]=tmps,sec[tmp-1]=tmpf;//
}
}
}
}
if((!(i&1))){//对奇偶行分别判断
if(ans1==1){//对奇偶队列产生的影响讨论
int tmp1=fir.front(),tmp2=sec.back();//
fir.pop_front(),fir.push_back(tmp2);
sec.pop_back(),sec.push_front(tmp1);
}
if(ans2==1){//对奇偶队列产生的影响讨论
int tmp1=sec.front(),tmp2=fir.back();//
sec.pop_front(),sec.push_back(tmp2);
fir.pop_back(),fir.push_front(tmp1);
}
swap(ans1,ans2);
vector <int>::iterator it;
for(it=ball[i].begin();it<ball[i].end();it++){
int v=*it;
if(v%2==0){//对删掉的木板是奇数列还是偶数列讨论
int tmp=v/2;
if(ans1==1)
{
int tmpf=fir[tmp],tmps=sec[tmp-1];
fir[tmp]=tmps,sec[tmp-1]=tmpf;//
}
if(ans2==1)//对奇偶队列产生的影响讨论
{
int tmpf=fir[tmp-1],tmps=sec[tmp];
fir[tmp-1]=tmps,sec[tmp]=tmpf;//
}
}
if(v%2==1){//对删掉的木板是奇数列还是偶数列讨论
int tmp=v/2+1;
if(ans1==1)
{
int tmpf=fir[tmp-1],tmps=sec[tmp-1];
fir[tmp-1]=tmps,sec[tmp-1]=tmpf;//
}
if(ans2==1)//对奇偶队列产生的影响讨论
{
int tmpf=fir[tmp-1],tmps=sec[tmp-1];
fir[tmp-1]=tmps,sec[tmp-1]=tmpf;//
}
}
}
}
}
int tnt=0;
if(ans1==1){
while(!sec.empty()){//
int font=fir.front(),sont=sec.front();
fir.pop_front(),sec.pop_front();
ans[font]=++tnt;ans[sont]=++tnt;
}
}
if(ans1==2){
while(!fir.empty()){//
int font=fir.front(),sont=sec.front();
fir.pop_front(),sec.pop_front();
ans[sont]=++tnt,ans[font]=++tnt;
}
}
for(int i=1;i<=w;i++){//
printf("%d\n",ans[i]);
}
}
T2:画画
首
先
观
察
题
目
,
发
现
走
过
去
再
走
回
来
的
操
作
像
极
了
N
O
I
P
某
年
的
d
p
题
首先观察题目,发现走过去再走回来的操作像极了NOIP某年的dp题
首先观察题目,发现走过去再走回来的操作像极了NOIP某年的dp题
所
以
运
用
套
路
走
两
次
所以运用套路走两次
所以运用套路走两次
我
开
始
的
想
法
是
因
为
只
会
往
右
走
或
往
下
走
,
所
以
能
走
就
一
定
走
完
,
后
来
发
现
走
两
次
,
就
以
为
不
行
,
结
果
发
现
没
法
判
断
只
有
1
条
路
径
的
情
况
我开始的想法是因为只会往右走或往下走,所以能走就一定走完,后来发现走两次,就以为不行,结果发现没法判断只有1条路径的情况
我开始的想法是因为只会往右走或往下走,所以能走就一定走完,后来发现走两次,就以为不行,结果发现没法判断只有1条路径的情况
正
解
:
正解:
正解:
对
于
第
一
条
路
径
:
能
往
下
走
就
走
,
不
能
走
就
往
右
走
对于第一条路径:能往下走就走,不能走就往右走
对于第一条路径:能往下走就走,不能走就往右走
对
于
第
二
条
路
径
,
能
往
右
走
就
往
右
走
,
不
能
走
就
往
下
对于第二条路径,能往右走就往右走,不能走就往下
对于第二条路径,能往右走就往右走,不能走就往下
对
于
重
合
的
点
乘
2
,
因
为
来
到
重
合
点
的
路
径
对
于
第
一
条
和
第
二
条
可
以
互
换
对于重合的点乘2,因为来到重合点的路径对于第一条和第二条可以互换
对于重合的点乘2,因为来到重合点的路径对于第一条和第二条可以互换
注
意
点
:
注意点:
注意点:
- 如果遇到连续的重合点只用乘1次2,因为对于连续的重合点,重合点之间的路径是唯一的,互换不会产生新的方案
- 收尾两个点的路径互换只能算一次,如果交换两次的话,和原来的方案是相同的(我代码中算的是尾点)
- 起点开始的连续路径不能计入答案(如果和我一样计算尾点的话)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;//
int a[N],b[N],suma[N],sumb[N];
int n;
ll ans=1,mod=1e9+7;//
bool flag=false,flag2=false;//
void dfs(int x,int y,int xx,int yy,bool ff){//ff用来判断连续的重合点
if(suma[x]>a[x]||sumb[y]>b[y]||suma[xx]>a[xx]||sumb[yy]>b[yy]) {if(x==xx&&y==yy) ans/=2;return;};
if(x!=xx||y!=yy){
flag=true;
}
if(x==n&&y==n&&xx==n&&yy==n){
flag2=true;
return;
}
if(suma[x]+1<=a[x]&&sumb[yy]+1<=b[yy]&&y+1<=n&&xx+1<=n){
suma[x]++,sumb[yy]++;
bool f=false;
if(x!=xx+1||y!=yy+1)sumb[y+1]++,suma[xx+1]++;
if(x==xx+1&&y+1==yy) {if(!ff) ans=ans*2%mod;f=true;};
dfs(x,y+1,xx+1,yy,f);
suma[x]--,sumb[yy]--;
if(x!=xx+1||y!=yy+1)sumb[y+1]--,suma[xx+1]--;
}
if(suma[x]==a[x]&&x+1<=n&&xx+1<=n&&sumb[yy]+1<=b[yy]){
suma[x+1]++,sumb[yy]++;
bool f=false;
if(x+1!=xx+1||y!=yy)suma[xx+1]++,sumb[y]++;
if(x+1==xx+1&&y==yy){if(!ff) ans=ans*2%mod;f=true;}
dfs(x+1,y,xx+1,yy,f);
suma[x+1]--,sumb[yy]--;
if(x+1!=xx+1||y!=yy)suma[xx+1]--,sumb[y]--;
}
if(sumb[yy]==b[yy]&&yy+1<=n&&y+1<=n&&suma[x]+1<=a[x]){
suma[x]++,sumb[yy+1]++;
bool f=false;
if(x!=xx||y+1!=yy+1)suma[xx]++,sumb[y+1]++;
if(x==xx&&y+1==yy+1){if(!ff) ans=ans*2%mod;f=true;}
dfs(x,y+1,xx,yy+1,f);
suma[x]--,sumb[yy+1]--;
if(x!=xx||y+1!=yy+1)suma[xx]--,sumb[y+1]--;
}
if(sumb[yy]==b[yy]&&suma[x]==a[x]&&x+1<=n&&yy+1<=n){
sumb[y]++;suma[xx]++;
bool f=false;
if(x+1!=xx||y!=yy+1)suma[x+1]++,sumb[yy+1]++;
if(x+1==xx&&y==yy+1){if(!ff) ans=ans*2%mod;f=true;}
dfs(x+1,y,xx,yy+1,f);
sumb[y]--;suma[xx]--;
if(x+1!=xx||y!=yy+1)suma[x+1]--,sumb[yy+1]--;
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
flag2=false;flag=false;
memset(suma,0,sizeof(suma));
memset(sumb,0,sizeof(sumb));
scanf("%d",&n); ans=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
}
suma[1]++,sumb[1]++;
dfs(1,1,1,1,1);
if(!flag2){
puts("0");
continue;
}
if(!flag){
puts("1");
continue;
}
printf("%lld\n",ans);
}
}
T3: 猫
洛古地址
(
洛
古
上
的
数
据
比
较
小
,
需
要
调
整
)
(洛古上的数据比较小,需要调整)
(洛古上的数据比较小,需要调整)
实
在
是
看
不
懂
写
滚
动
数
组
的
是
怎
么
过
的
,
当
多
了
一
位
j
时
,
不
知
道
为
什
么
j
−
1
的
决
策
点
可
以
直
接
给
j
实在是看不懂写滚动数组的是怎么过的,当多了一位j时,不知道为什么j-1的决策点可以直接给j
实在是看不懂写滚动数组的是怎么过的,当多了一位j时,不知道为什么j−1的决策点可以直接给j
本
题
解
比
较
好
理
解
,
但
空
间
较
大
本题解比较好理解,但空间较大
本题解比较好理解,但空间较大
d
p
[
i
]
[
j
]
表
示
取
[
1
,
i
]
只
猫
,
用
j
个
管
理
员
的
最
小
等
待
时
间
dp[i][j]表示取[1,i]只猫,用j个管理员的最小等待时间
dp[i][j]表示取[1,i]只猫,用j个管理员的最小等待时间
d
p
[
i
]
[
j
]
=
d
p
[
k
]
[
j
−
1
]
+
(
j
−
k
)
∗
T
[
j
]
−
(
s
u
m
[
j
]
−
s
u
m
[
k
]
)
dp[i][j]=dp[k][j-1]+(j-k)*T[j]-(sum[j]-sum[k])
dp[i][j]=dp[k][j−1]+(j−k)∗T[j]−(sum[j]−sum[k])
移
一
下
项
,
发
现
d
p
[
k
]
[
j
−
1
]
+
s
u
m
[
k
]
=
T
[
j
]
∗
k
+
d
p
[
i
]
[
j
]
−
j
∗
T
[
j
]
+
s
u
m
[
j
]
移一下项,发现dp[k][j-1]+sum[k]=T[j]*k+dp[i][j]-j*T[j]+sum[j]
移一下项,发现dp[k][j−1]+sum[k]=T[j]∗k+dp[i][j]−j∗T[j]+sum[j]
因
为
T
[
j
]
单
调
递
增
,
所
以
我
们
可
以
维
护
一
个
凸
包
,
但
由
于
是
二
维
,
所
以
要
维
护
多
个
凸
包
,
每
次
判
断
队
首
的
斜
率
是
否
小
于
T
[
j
]
,
是
就
弹
出
队
列
,
直
到
大
于
T
[
j
]
(
因
为
T
[
j
]
单
调
递
增
,
所
以
后
面
也
不
会
再
用
到
弹
出
的
元
素
)
因为T[j]单调递增,所以我们可以维护一个凸包,但由于是二维,所以要维护多个凸包,每次判断队首的斜率是否小于T[j],是就弹出队列,直到大于T[j](因为T[j]单调递增,所以后面也不会再用到弹出的元素)
因为T[j]单调递增,所以我们可以维护一个凸包,但由于是二维,所以要维护多个凸包,每次判断队首的斜率是否小于T[j],是就弹出队列,直到大于T[j](因为T[j]单调递增,所以后面也不会再用到弹出的元素)
接
下
来
取
出
队
首
转
移
即
可
接下来取出队首转移即可
接下来取出队首转移即可
(实在不明白为什么多一维之后决策点还是能直接用!!!)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,p;
int dis[N],T[N];
typedef long long ll;
ll dp[50005][1003],q[50005][1003],hd[50005],tl[50005],sum[N];
inline double slope(int tt,int a,int b){
return double(dp[a][tt]+sum[a]-dp[b][tt]-sum[b])/(a-b);
}
int main(){
scanf("%d%d%d",&n,&m,&p);
dis[1]=0;
for(int i=2;i<=n;i++){
int d;
scanf("%d",&d);
dis[i]=dis[i-1]+d;
}
for(int i=1;i<=m;i++){
int h,t;
scanf("%d%d",&h,&t);
T[i]=t-dis[h];
}
sort(T+1,T+m+1);
for(int i=1;i<=m;i++){
sum[i]=sum[i-1]+T[i];
}
for(int j=0;j<=m;j++){
hd[j]=1,tl[j]=0;
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=p;j++){
while(hd[j-1]<tl[j-1]&&slope(j-1,q[j-1][hd[j-1]],q[j-1][hd[j-1]+1])<=T[i]) hd[j-1]++;//
int nxt=q[j-1][hd[j-1]];
dp[i][j]=dp[nxt][j-1]+(i-nxt)*T[i]-(sum[i]-sum[nxt]);
while(hd[j]<tl[j]&&slope(j,q[j][tl[j]],q[j][tl[j]-1])>slope(j,q[j][tl[j]],i)) tl[j]--;
q[j][++tl[j]]=i;
}
}
printf("%lld",dp[m][p]);
}

本文探讨了三个游戏编程难题:使用STL解决球落地点预测、通过动态规划和路径选择完成画画任务,以及采用凸包优化的猫管理员分配问题。文章详细介绍了每道题目的解题思路和关键步骤,为游戏开发和算法爱好者提供了丰富的实战经验。
453

被折叠的 条评论
为什么被折叠?



