题意:
就是说有一个a数组,然后你可以把这个数组分成任意段,然后每段有xi个,那么这个xi要么放在这一段数字的前面或者后面。然后每一段都放好之后,把你分成的这些段数字再合起来形成了b数组。现在给你b数组,问你这个b数组是否可以由某个a数组拆分再合并之后得到来的。
思考:
- 当时看到这题,总感觉是暴力?就是贪心选择某个数字是第一段数字的x,但是我发现确定好第一个之后,后面的还是无法确定。所以这个思路不能想了。
- 然后我就想,感觉是不是dp呢,然后我就想怎么dp呢?发现也不太好dp,但是我又思考了一会发现,每个点如果作为xi的话就可以代表左边和右边两个区间,所以那么题意就可以转化为:给你一些区间,然后让你选择一些区间是否能正好拼成[1,n]这个区间。同时每个点代表的两个区间也不会同时选,因为中间有个点重合了,所以就转化成了那样的题意。
- 那么转化成这个感觉就像一个经典题目了,又和dp联想了一下,感觉想到了之前做的这道题:Cleaning Shifts。区间问题经常用dp去更新,所以就直接按区间的ed排序,然后遍历区间dp转移状态即可。
- 反正也是多多思考,多思考一会,多联想联想以前做的题目什么的,去转化题意,把题目往各种可以延申的地方去转化。
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;
int T,n,m,k;
int va[N];
int dp[N];
vector<PII > v;
bool cmp(PII A,PII B)
{
return A.se<B.se;
}
signed main()
{
IOS;
cin>>T;
while(T--)
{
cin>>n;
v.clear();dp[0] = 1;
for(int i=1;i<=n;i++) dp[i] = 0;
for(int i=1;i<=n;i++)
{
cin>>va[i];
int l = i-va[i],r = i+va[i];
if(l>=1) v.pb({
l,i});
if(r<=n) v.pb({
i,r});
}
sort(v.begin(),v.end(),cmp);
for(auto t:v) dp[t.se] |= dp[t.fi-1];
dp[n]?cout<<"YES\n":cout<<"NO\n";
}
return 0;
}
总结:
多多思考,多多联想。