一,思路:
1.首先映射一下,将在1的位置上,表示映射成在0位置上(便于后面取余判断位置操作),然后 dp思路,用f[i][j]状态数组,表示表示在第 i 步的时候, j 这个位置是那一步走过来的(其实如果你你真正理解这题的话,除了是由 f [i -1] [ j ],这个状态转移过来的外,其他的都是 0)。
2.状态转移方程:(1)f[i][(j+x)%n]=i,(2)f[i][((j-x)%n+n)%n]=i;
3.具体看代码注释
二,代码:
#include<iostream>
using namespace std;
const int N=5010;
int f[N][N];
int main(){
int n,m;
cin>>n>>m;
int x;
cin>>x;
//初始化第一步走之后的状态变化
f[1][x%n]=1;
f[1][(-x%n+n)%n]=1;
for(int i=2;i<=m;i++){
//输入操作步数
cin>>x;
// 遍历(0,n-1)所有位置,看有没有那个点是上一一步,更新过来的
//(题目要求,必须一个操作一个操作的执行)
for(int j=0;j<n;j++){
//判断我这一步,能否从 j 这个点,走 x步。
if(f[i-1][j]==i-1){
f[i][(j+x)%n]=i;
f[i][((j-x)%n+n)%n]=i;
}
}
}
//判断在第m步操作后,0这个是否是,第m次操作后更新过来的(只看结果,不看过程)
if(f[m][0]==m) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}
三,很明显的发现,其实我们每次更新只用到了上一层数组的值,所以我们有可以优化成一维的机会,但是你会发现更新成一维后,我们进行状态转移会影响到我们一些未更新的点或以更新的点,所以我们可以准备两个数组 a , b,一个表示上一步操作后的状态,一个表示这次状态转移后的状态。
四,代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N=5010;
int a[N];
int b[N];
int main(){
int n,m;
cin>>n>>m;
int x;
cin>>x;
a[x%n]=1;
a[(-x%n+n)%n]=1;
for(int i=2;i<=m;i++){
cin>>x;
for(int j=0;j<n;j++){
if(a[j]==i-1){
b[(j+x)%n]=i;
b[((j-x)%n+n)%n]=i;
}
}
//保存这次状态转移后的状态
memcpy(a,b,sizeof a);
}
if(a[0]==m) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}