Codeforces Round #687 (Div. 1, based on Technocup 2021 Elimination Round 2) D - Cakes for Clones DP
D. Cakes for Clones
You live on a number line. You are initially (at time moment 𝑡=0) located at point 𝑥=0. There are 𝑛 events of the following type: at time 𝑡𝑖 a small cake appears at coordinate 𝑥𝑖. To collect this cake, you have to be at this coordinate at this point, otherwise the cake spoils immediately. No two cakes appear at the same time and no two cakes appear at the same coordinate.
You can move with the speed of 1 length unit per one time unit. Also, at any moment you can create a clone of yourself at the same point where you are located. The clone can't move, but it will collect the cakes appearing at this position for you. The clone disappears when you create another clone. If the new clone is created at time moment 𝑡, the old clone can collect the cakes that appear before or at the time moment 𝑡, and the new clone can collect the cakes that appear at or after time moment 𝑡.
Can you collect all the cakes (by yourself or with the help of clones)?
Input
The first line contains a single integer 𝑛 (1≤𝑛≤5000) — the number of cakes.
Each of the next 𝑛 lines contains two integers 𝑡𝑖 and 𝑥𝑖 (1≤𝑡𝑖≤109, −109≤𝑥𝑖≤109) — the time and coordinate of a cake's appearance.
All times are distinct and are given in increasing order, all the coordinates are distinct.
Output
Print "YES" if you can collect all cakes, otherwise print "NO".
Examples
input
3
2 2
5 5
6 1
output
YES
input
3
1 0
5 5
6 2
output
YES
input
3
2 1
5 5
6 0
output
NO
Note
In the first example you should start moving towards 5 right away, leaving a clone at position 1 at time moment 1, and collecting the cake at position 2 at time moment 2. At time moment 5 you are at the position 5 and collect a cake there, your clone collects the last cake at time moment 6.
In the second example you have to leave a clone at position 0 and start moving towards position 5. At time moment 1 the clone collects a cake. At time moment 2 you should create a new clone at the current position 2, it will collect the last cake in future. You will collect the second cake at position 5.
In the third example there is no way to collect all cakes.
翻译
有一条线,你站在0这个点。
现在有n个事件,表示有一个蛋糕将会在t[i]秒出现在x[i]的位置,你每秒可以移动1,然后你的任务是吃掉所有的蛋糕。
你现在有个功能,就是你可以在你的位置生成一个克隆,这个克隆不能移动,可以一直呆在原地吃蛋糕,但是同时只能存在一个蛋糕。
问你是否存在一个方案吃掉所有的蛋糕。
题解
考虑最最暴力的dp, dp[i][j]表示吃掉1~i,克隆体在第j个位置的最小花费是多少。
3方的dp
for (int i=1;i<=n;i++) {
for (int j=i+1;j<=n;j++) {
for (int k=i-1;k<=n;k++) {
if (j!=k) {
dp[i][j]=min(dp[i][j], cost + dp[i-1][k]);
}
}
// 转移一下 j==k 的情况,就可以到处放克隆体
}
}
n3肯定是会TLE的,优化一下发现有些状态是不需要的,我们不需要存储克隆体在j的最小花费。
我们定义minTime[i],表示我们在吃完了[1,i-1]所有蛋糕,且我在x[i]的最小花费是多少
dp[i][j]表示吃完了[1,i]的所有蛋糕,人在x[i],分身放在x[j]是否可行。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5005;
int n;
long long t[maxn],x[maxn];
long long dp[maxn][maxn],minTime[maxn];
long long dis(long long x,long long y) {
return abs(x-y);
}
int main() {
cin>>n;
for (int i=1;i<=n;i++) {
cin>>t[i]>>x[i];
minTime[i] = 1e17;
}
minTime[0]=0;
dp[0][0]=1;
for (int i=1;i<=n;i++) {
if (x[i] == 0) {
dp[0][i]=1;
}
}
for (int i=1;i<=n;i++) {
// 从x[i-1]点出发, 不靠分身
if (minTime[i-1] <= t[i-1]) {
minTime[i] = min(minTime[i], max(minTime[i-1] + dis(x[i], x[i-1]), t[i-1]));
if (minTime[i] <= t[i]) {
dp[i][i]=1;
}
}
// 站在i点,分身放在j点,又回来
for (int j=i;j<=n;j++) {
if (minTime[i]+2*dis(x[i],x[j])<=t[i]) { // 过去又回来
dp[i][j]=1;
}
}
// 从i-1点出发,人在i,分身仍然留在j
for (int j=i+1;j<=n;j++) {
if (dp[i-1][j] && t[i-1] + dis(x[i], x[i-1]) <= t[i]) {
dp[i][j] = 1;
}
}
// 从i-1 -> j放下分身,i-1 -> i吃
for (int j=i+1;j<=n;j++) {
if (minTime[i-1]<=t[i-1] && max(t[i-1],minTime[i-1]+dis(x[j],x[i-1]))+dis(x[j],x[i])<=t[i]) {
dp[i][j]=1;
}
}
// 2 dp[i-1][i]是合法的 -> dp[i+1][i] / minTime[i+1]
if (dp[i-1][i] && i+1 <= n) {
minTime[i+1] = min(minTime[i+1], max(t[i-1] + dis(x[i+1],x[i-1]), t[i]));
if (minTime[i+1] <= t[i+1]) {
dp[i+1][i+1]=1;
}
// 先去j,等待分身i吃完,放下分身,再去i+1
for (int j=i+1;j<=n;j++) {
if (max(t[i-1]+dis(x[j],x[i-1]),t[i])+dis(x[j],x[i+1]) <= t[i+1]) {
dp[i+1][j] = 1;
}
}
}
}
if (dp[n][n] == 1 || dp[n-1][n] == 1) {
cout<<"YES"<<endl;
} else {
cout<<"NO"<<endl;
}
}