算术整除——扩散型dp

题目描述

给定长度为 n n n 的整数序列和 k k k,要求在相邻的两个数字之间插入加(‘ + + +’)或减(‘ − - ’)操作符(不能改变顺序),使得算术结果是k的整数倍。

例如序列为: 17 , 5 , − 21 , 15 , k = 7 17, 5, -21,15,k=7 17,5,21,15,k=7,那么 17 + 5 + ( − 21 ) − 15 = − 14 17+5+(-21)-15=-14 17+5+(21)15=14 7 7 7 的整数倍。

分析

考虑 dp

d p i , j dp_{i,j} dpi,j 表示前 i i i 个数能否凑出 j j j,可以就为 1 1 1,否则为 0 0 0,如果 d p i , j dp_{i,j} dpi,j 可行,则 d p i + 1 , ( j + a i ) % k dp_{i+1,(j+a_i)\%k} dpi+1,(j+ai)%k 也行(这里 % \% % 表示取模),同理 d p i + 1 , ( j − a i + k ) % k dp_{i+1,(j-a_i+k)\%k} dpi+1,(jai+k)%k 也可行(这里加 k k k 是为了防负数)。

初始状态为 d p 0 , 0 dp_{0,0} dp0,0 为可行,因为前 0 0 0 个数可以凑出 0 0 0

答案就是 d p n , 0 dp_{n,0} dpn,0

考试时往收集型 dp 想了,写了好久

代码

#include 

using namespace std;

const int N = 10005;
int n, k, a[N], dp[N][1005];

int main(){
	cin >> n >> k;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
		a[i] = (a[i] + k) % k;
	}
	dp[0][0] = 1;
	for(int i = 0; i < n; i ++){//枚举已有状态,去推出未知状态
		for(int j = 0; j < k; j ++){//因为取模过了所以不可能大于k
			if(dp[i][j]){
				dp[i + 1][(j + a[i + 1]) % k] = 1;
				dp[i + 1][(j - a[i + 1] + k) % k] = 1;
			}
		}
	}
	if(dp[n][0]){
		cout << "Divisible"; 
	}else{
		cout << "Not divisible\n";
	}
	return 0;
}

你可能感兴趣的:(算法,c++,动态规划)