题意:沙滩从左往右由海岸线,n米的海和一个在n+1的点的岛组成。一个人测量了从海岸线(1,2,dots,n)米到岛屿的海的深度,并保存在(d)数组中,这个海有潮汐,潮汐的强度取决于参数(k),参数(k)影响了海的深度,对于从时间(t = 0)开始,先k秒,每一秒,潮汐导致所有的深度+1,然后再k秒,潮汐每秒导致所有的深度都减1。这个过程不断地持续着,例如对于索引从0开始的数组(p = [0, 1, 2,...,k - 2, k - 1, k, k - 1, k - 2, ..., 2, 1]),长度为2k,在时间t,第i米的深度为(di+p[t\,mod\,k])。这个人从海岸线开始游到岛屿,每一秒,他可以前进或者呆在原地,时间t + 1,这个人有一个被淹死的水的深度的参数(l),如果(当且深度>l),那么这个人就会被淹死。求是否存在一种游的方式,使得这个人可以游到岛屿。
分析:可以看到(n)只有100,题目中每次游动的方式又只有两种,可以想到用搜索做。在比赛的时候,我定义的状态是(位置和时间),这样不能有效地检测出不能游到海岸的方案。因为我很难判断两个状态是否是一致的,如果对于当且状态来说,之前出现过一个一样的状态,那么说明,经过了时间2 * k,还在当前点,说明状态的转移之间出现了环,因此这个搜索的分支是返回false的。官方题解里用位置,潮汐导致的增量,潮汐是在上升还在下降定义状态,然后用一个set存储之前出现过的状态,如果在set里面发现了一个和当且状态一样的状态,那么就说明过了时间2 * k,还是当且的状态,返回false。然后,在搜索的时候,我们有两种方式游,一种是呆在原地即d[pos] + tide <= l && dfs(pos, tide, down),另一种是向下一个位置游动d[pos + 1] + tide <= l && dfs(pos + 1, tide, down)。当然,还有两个小细节,比如d[0]和d[n + 1]表示海岸线和岛屿的深度,这个直接赋值一个很小的负数即可,只要能游过去即可。还有潮汐导致的深度变换应该是(1, 2, ..., k, k - 1, k - 2, ..., 0),因为p数组中当t是k的倍数的时候,会直接模到0,容易混淆。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <tuple>
#include <algorithm>
using namespace std;
const int N = 105;
int d[N];
struct Node
{
int pos, tide;
bool down;
bool operator<(const Node& rhs)const
{
if (pos != rhs.pos)
return pos < rhs.pos;
else
{
if (tide != rhs.tide)
return tide < rhs.tide;
else
return down < rhs.down;
}
}
};
int n, k, l;
set<Node> v;
bool dfs(int pos, int tide, bool down)
{
//游到岛屿
if (pos > n) return true;
//集合中是否存在一个和当且状态一样的状态
if (v.find({ pos, tide, down }) != v.end()) return false;
//插入到set中
v.insert({ pos, tide, down });
tide += down ? -1 : +1;
if (tide == 0) down = false;
if (tide == k) down = true;
if (d[pos] + tide <= l && dfs(pos, tide, down))
return true;
if (d[pos + 1] + tide <= l && dfs(pos + 1, tide, down))
return true;
return false;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d%d", &n, &k, &l);
for (int i = 1; i <= n; ++i)
scanf("%d", &d[i]);
d[0] = d[n + 1] = -k;
if (dfs(0, 0, false))
puts("Yes");
else
puts("No");
v.clear();
//memset(d, 0, sizeof d);
}
return 0;
}