[JOI 2020 Final] スタンプラリー 3
\(\text{Problem}:\)[JOI 2020 Final] スタンプラリー 3
\(\text{Solution}:\)
显然,移动方式形如:顺时针走到某个端点,再逆时针走到某个端点 \(...\) 特别的,我们设起始点是原点,则每次改变走的方向都会经过原点。
那么我们破环为链,记逆时针是往左走,顺时针是往右走。设 \(F_{i,j,k,0/1}\) 表示往左经过了 \(i\) 个点,往右经过了 \(j\) 个点,现在是 \(k\) 时间,在左/右端点的最大答案。
然而 \(t_{i}\leq 10^9...\) 如果以时间为第三维状态,要离散化,而且转移非常麻烦。对于一种收集方式来,答案最大和时间最短是等价的,所以套路地改变 \(dp\) 状态,设 \(F_{i,j,k,0/1}\) 表示往左经过了 \(i\) 个点,往右经过了 \(j\) 个点,已经收集了 \(k\) 个物品,在左/右端点的最小时间,然后暴力转移即可,时间复杂度 \(O(n^3)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
//pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=210;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,L,a[N],t[N],F[N][N][N][2];
signed main()
{
n=read(), L=read();
for(ri int i=1;i<=n;i++) a[i]=read(); a[n+1]=L;
for(ri int i=1;i<=n;i++) t[i]=read();
memset(F,0x3f,sizeof(F));
F[0][0][0][0]=F[0][0][0][1]=0;
for(ri int i=0;i<=n;i++)
for(ri int j=0;i+j<=n;j++)
for(ri int k=0;k<=i+j;k++)
{
if(F[i][j][k][0]<=1e18)
{
int can=(a[n-i+1]-a[n-i]+F[i][j][k][0]<=t[n-i]);
F[i+1][j][k+can][0]=min(F[i+1][j][k+can][0],F[i][j][k][0]+a[n-i+1]-a[n-i]);
can=(a[j+1]+L-a[n-i+1]+F[i][j][k][0]<=t[j+1]);
F[i][j+1][k+can][1]=min(F[i][j+1][k+can][1],F[i][j][k][0]+a[j+1]+L-a[n-i+1]);
}
if(F[i][j][k][1]<=1e18)
{
int can=(a[j+1]-a[j]+F[i][j][k][1]<=t[j+1]);
F[i][j+1][k+can][1]=min(F[i][j+1][k+can][1],F[i][j][k][1]+a[j+1]-a[j]);
can=(a[j]+L-a[n-i]+F[i][j][k][1]<=t[n-i]);
F[i+1][j][k+can][0]=min(F[i+1][j][k+can][0],F[i][j][k][1]+a[j]+L-a[n-i]);
}
}
int res=0;
for(ri int k=0;k<=n;k++)
for(ri int i=0;i<=n;i++)
for(ri int j=0;i+j<=n;j++)
for(ri int x=0;x<2;x++)
if(F[i][j][k][x]<=1e18) res=max(res,k);
printf("%lld\n",res);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。