【题目】
一个给定的正整数序列,在每个数之前都插入+号或-号后计算它们的和。所有结果中至少有一个可被整数k整除,我们则称此正整数序列可被k整除。
现判断一个正整数序列是否能被k整除。输入的第一行包含两个数:N(2 < N < 10000)和k(2 < k< 100),其中N代表一共有N个数,k代表被除数。
第二行给出序列中的N个整数,这些整数的取值范围都0到10000之间(可能重复)。
【题目链接】
http://noi.openjudge.cn/ch0206/3531/
【分析】
1. 所谓递推就是一个问题与其子问题有一定的关系,为求解问题,应从子问题入手并记录子问题能提供的数据(及其状态),通过子问题的数据/状态逐步将子问题扩展为原问题。
2. 该题的子问题就是该序列前i个数能否被k整除。
3. 再来看子问题状态,若仅仅提供该问题能否被k整除无法确定状态,根据mod运算的性质【(a+b)%k = (a%k+b%k)%k】不难想到,任意状态的值并不需要均求出来,而且任意两个状态如果相差k的整数倍则可将这两个状态视为等价,所以任意状态可映射到其对k取模之后的余数上去,当且仅当两个状态对k取模后余数相等可将这两个状态视为等价。由于一个子问题可能映射到多个余数,而k为2~100,所以定义子问题状态【bool 数组 b[i][j]的值表示序列前i个数所包含的状态除k余数是否可能为j】
4. 再来看状态转移,若b[i-1][j]=1则b[i][(j+a[i]) % k] = 1, b[i][(j-a[i]+k) % k] = 1;初始情况则是b[1][a[1]] = 1;
【代码】
1 #include <iostream> 2 using namespace std; 3 int n,i,j,k; 4 int a[10010]; 5 bool b[10010][110]; 6 int main() 7 { 8 cin>>n>>k; 9 for(i = 1; i <= n; i++) cin>>a[i],a[i]%=k; 10 b[1][a[1]] = 1; 11 for(i = 2; i <= n; i++) 12 for(j = 0; j < k; j++) 13 if(b[i-1][j]) 14 b[i][(a[i]+j)%k] = 1, 15 b[i][(j-a[i]+k)%k] = 1; 16 if(b[n][0]) cout<<"YES"; 17 else cout<<"NO"; 18 }