第五届图灵杯
中级组
T1
我们考虑向后转移,类似 \(dp\)
维护两个等差数列的公差,末项,即可知道等差数列的下一个数
我们考虑爆搜
一个数要么放到第一个序列,要么放到第二个序列
我们贪心,能放到原来的序列就不要新开序列
新序列就算去掉了开头也是等差序列,所以直接放在第一个序列就可以
然后直接爆搜分情况讨论
\(O(2^n \to n)\)
const int N=5e5+10;
int n;
int a[N];
bool dfs(int now,int ed1,int ed2,int d1,int d2)
{
if(now==n+1) return 1;
//第一个序列空
if(!(~ed1)) return dfs(now+1,a[now],ed2,d1,d2);
//第二个序列空 能放第一个就放第一个
if(!(~ed2) && ~d1 && a[now]==ed1+d1) return dfs(now+1,a[now],ed2,d1,d2);
bool ok=0; //第二个序列为空,接在第二个
if(!(~ed2)) ok|=dfs(now+1,ed1,a[now],d1,d2);
if(ok) return 1; //第一个序列 1 个数
if(!(~d1) && ~ed1) ok|=dfs(now+1,a[now],ed2,a[now]-ed1,d2);
if(ok) return 1; //第二个序列 1 个数
if(!(~d2) && ~ed2) ok|=dfs(now+1,ed1,a[now],d1,a[now]-ed2);
if(ok) return 1; //接在第一个
if(~d1 && ~ed1 && a[now]==ed1+d1) ok|=dfs(now+1,a[now],ed2,d1,d2);
if(ok) return 1; //接在第二个
if(~d2 && ~ed2 && a[now]==ed2+d2) ok|=dfs(now+1,ed1,a[now],d1,d2);
return ok;
}
void solve()
{
n=fr();
for(int i=1;i<=n;i++) a[i]=fr();
puts(dfs(1,-1,-1,-1,-1)?"Yes":"No");
}
int main()
{
int T=fr();
while(T--) solve();
return 0;
}
T2
显然的 \(bfs\) 最短路
一个小优化,对于车和象,每个状态点最多进过一次,一旦从内向外的扩散路径上有点被扩散过,直接 \(break\) 即可
\(O(nm)\)