思路参考:AtCoder Beginner Contest 221【A - G】 - Kanoon - 博客园
G - Jumping sequence
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, M = 3.6e6+5;
int n, a, b, d[N], sum;
bitset<M> bit[N];
bool addx[N], addy[N]; //记录每一步是否有add
/* 思路:考虑把二维拉伸成一维,假设只有一个方向轴,每次可以走+di或者-di,
如果既能走到a+b,又能走到a-b,那么说明可以(因为这样就说明能分成两部分,一部分是a,一部分是b),否则不行。
考虑到走+di或者-di可能会到达负坐标,不好处理,所以右移处理,每次额外走+di距离,变成走+2*di或者0,目的地变成a+b+sum和a-b+sum
然后再把走的距离/2,每次走+di或者0,目的地坐标也相应/2(注意这里已经可以排除部分情况,如果/2不是整数,说明肯定到不了)
然后进行bit压缩的dp递推,算出所有可到的地方,包含两个目的地就说明Yes,否则No
重点是还原路径:考虑把之前的坐标变换反向映射一下,具体见下。
*/
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>a>>b; for(int i=1; i<=n; i++) cin>>d[i], sum+=d[i];
for(int i:{a+b+sum, a-b+sum}) //检查两个目的地是否符合题意,不符合直接No,结束
if(i%2 || i/2<0 || i/2>sum) {cout<<"No"; return 0;} //是奇数,或者越界都不行
int x = (a+b+sum)/2, y=(a-b+sum)/2; //两个目的地,必须都能到达
bit[0][0] = 1; //起点init
for(int i=1; i<=n; i++)
bit[i] = bit[i-1]|(bit[i-1]<<d[i]); //可以不动,也可以动
if(!bit[n][x] || !bit[n][y]) {cout<<"No"; return 0;} //其中一个到不了,结束
cout<<"Yes"<<'\n'; //否则就一定能到达
for(int i=n; i>=1; i--){ //原路返回找路径
if(!bit[i-1][x]) {addx[i]=1; x-=d[i];} //i和i+1位置不同,说明i的时候动了,现在原路返回
if(!bit[i-1][y]) {addy[i]=1; y-=d[i];}
}
for(int i=1; i<=n; i++){ //遍历两个add数组,映射得到原路径
cout<<(addx[i]? (addy[i]? 'R':'U') : (addy[i]? 'D':'L'));
}
}
/* 这里简述一下映射关系:
不处理 方向变形 右移,除以2
方向 (a,b) ---> (a+b,a-b) ---> ((a+b+sum)/2, (a-b+sum)/2)
右 (1,0) (1,1) (1,1)
左 (-1,0) (-1,-1) (0,0)
上 (0,1) (1,-1) (1,0)
下 (0,-1) (-1,1) (0,1)
*/