`

一个用DP求解的问题的分析

阅读更多
问题:Consider an arbitrary sequence of integers. One can place + or - operators between integers in the sequence, thus deriving different arithmetical expressions that evaluate to different values. Let us, for example, take the sequence: 17, 5, -21, 15. There are eight possible expressions:

17 + 5 + -21 + 15 =  16
17 + 5 + -21 - 15 = -14
17 + 5 - -21 + 15 =  58
17 + 5 - -21 - 15 =  28
17 - 5 + -21 + 15 =   6
17 - 5 + -21 - 15 = -24
17 - 5 - -21 + 15 =  48
17 - 5 - -21 - 15 =  18

We call the sequence of integers divisible by K if + or - operators can be placed between integers in the sequence in such way that resulting value is divisible by K. In the above example, the sequence is divisible by 7 (17+5+-21-15=-14) but is not divisible by 5.
You are to write a program that will determine divisibility of sequence of integers.

Input
There are multiple test cases, the first line is the number of test cases.
The first line of each test case contains two integers, N and K (1 ≤ N ≤ 10000, 2 ≤ K ≤ 100) separated by a space.

The second line contains a sequence of N integers separated by spaces. Each integer is not greater than 10000 by it's absolute value.

Output
Write to the output file the word "Divisible" if given sequence of integers is divisible by K or "Not divisible" if it's not.

Sample Input
2
4 7
17 5 -21 15
4 5
17 5 -21 15
Sample Output
Divisible
Not divisible
这道题目就是在给定的一堆数中,添加+ -号,判断一下结果是否能整除给定的K.
这道题首先给人的感觉是穷举/搜索,但是给的数在10000以内,穷举的话是2^n,最坏情况10000个数,如果不可整除,计算出来时间是相当长的.
而把指数级复杂度变为多项式级别的可以考虑动态规划.
这道题的动态规划信息非常隐蔽,因为如果直接把这些数的运算划分为子问题的话,这些子问题都将是不重复的.而动态规划是通过记住子问题的最优解来避免重复计算而提高效率的.
但仔细分析这道题,我们发现其实没有必要真正求出表达式的值,我们只要把每一步计算结果
的对k的余数记住就行了.这样每一步的计算结果就在0~K之间了.如果我们把这个计算过程表示为树形结构的话
               a1
         /           \
      a1-a2          a1+a2
      /  \          /      \
a1-a2-a3 a1-a2+a3 a1+a2-a3 a1+a2+a3
...................................
这样第j层就会有2^j个计算的结果,而如果我们每次计算的时候都对k求模,这样这2^j个值
都在0~k-1之间,而如果一层中的两个节点的值一样,那么显然可以在计算第一个节点的值是否能被k整除后把这个结果保存下来,再遇到这样同一个值的可以直接查找到,事实上这样重复的节点计算是相当多的--(指数级别2^j的数在常量0~k范围).这样我们把一棵树的宽度约束在了1~k的范围了,而高度是N,所以复杂度变成了多项式级别O(k*N)了:
代码如下:
#include<iostream>
#include<string>
using namespace std;

int flag[10005][105];
int a[1005];
int n,K,N;
//@return 返回是否可以整除K,@parm level递归形成二叉数的层数,vaule为该层一个计算出节点的值
int f(int level,int value){
	if(flag[level][value] != -1) return flag[level][value];//如果已经记录过了,就直接返回
	int check,t1,t2;
	t1 = (value + a[level]) % K;//进行+运算并对K取余数
	t2 = (value - a[level]) % K;//进行-运算并对K取余数
	t1 = t1 < 0 ? t1 + K : t1;//余数都改成正的
	t2 = t2 < 0 ? t2 + K : t2;

	if(level == N)  check = (value % K == 0) ? 1 : 0;//如果第N层,递归出口,判断 value 是否可以整除K
	else if(f(level + 1,t1) == 1) check = 1;//如果level + 1层计算结果可以整除K,则标记check为1,同时可以跳过另一个分支,因为0-a,0+a对最终判断否是否可以整除K的效果是一样的
	else if( f(level + 1,t2) == 1 ) check = 1;
	else check = 0;//左右分支均不可以整除 check=0;
        return flag[level][value] = check;//返回并记录本层的check值
}

int main(){
  int m;
  int i,j;
  string result;
  cin >> n;
  for(i = 0; i < n; i++){
	  cin >> N >> K;
      for(j = 0; j < N; j++){
			cin >> a[j];
			if((a[j] %= K) < 0) a[j] += K;
	  }

	  for(j = 0; j <= N; j++)
		  for(m = 0; m <= K; m++)
		      flag[j][m] = -1;


	    result = f(1,a[0]) == 1 ? "Divisible" : "Not divisible" ;
		cout << result << endl;
  }
  return 0;
}
分享到:
评论

相关推荐

    8.5求解最长公共子序列问题-求dp.pdf

    程序代码提供了一个 C++ 实现,通过包含头文件和宏定义来初始化动态规划数组 dp 的大小,并定义了求解最长公共子序列长度的 LCSlength 函数和反向构建最长公共子序列的 Buildsubs 函数。main 函数中初始化了两个示例...

    ConsoleApplication20_算法设计与分析之动态规划求解硬币问题_

    在这个“算法设计与分析之动态规划求解硬币问题”的案例中,我们将探讨如何使用动态规划方法来解决一个经典的组合优化问题:硬币找零问题。 硬币找零问题的基本设定是这样的:你有一组不同面值的硬币,目标是找出...

    约瑟夫问题java求解

    约瑟夫问题(Josephus Problem)是一个著名的理论问题,源于公元前一世纪犹太历史学家约瑟夫·弗拉维乌斯的叙述。该问题在计算机科学和算法设计中有着广泛的应用,因为它涉及到循环和计数的策略。在这个问题中,人们...

    实验2. 动态规划法求解最长公共子序列问题与0-1背包问题.doc

    * 从算法分析与设计的角度,对基于DP法求解问题的理解 实验步骤 步骤 1:理解问题,给出问题的描述。 步骤 2:算法设计,包括策略与数据结构的选择 步骤 3:描述算法,可以采用伪代码、流程图等形式。 步骤 4:...

    动态规划求解矩阵数乘问题

    在矩阵乘法中,我们可以构建一个二维数组dp,其中dp[i][j]表示计算前i行和前j列的子矩阵乘积所需的最小运算次数。我们可以通过比较计算AB然后与第i行乘以C的代价和计算BC然后与第i列乘以A的代价来填充这个数组。 ...

    数学建模中动态规划问题的原理分析及MATLAB求解过程

    ### 数学建模中动态规划问题的原理分析及MATLAB求解过程 #### 一、动态规划概述 ##### 1.1 动态规划的发展及研究内容 动态规划(Dynamic Programming,简称DP)是由美国数学家Richard Bellman于20世纪50年代提出...

    数位DP.pdf

    在数位DP模板中,通常包含一个数字处理函数,这个函数的作用是将一个整数分解为它的每一位数字,并且为后续的DFS操作做好准备。通过不断递归调用DFS函数,我们可以从高位到低位处理每一位数字,直到完成整个数字的...

    DP.rar_DP_dp算法_omp

    在提供的文件列表中,有一个名为"DP.m"的文件,这很可能是一个MATLAB程序,实现了DP_OMP算法。MATLAB是一种强大的数值计算和可视化工具,常用于科学计算和工程应用。这个文件可能包含了算法的详细实现,包括初始化、...

    约瑟夫斯问题求解

    约瑟夫斯问题(Josephus Problem)是一个著名的理论问题,源于古罗马历史学家约瑟夫斯的叙述。在历史上,约瑟夫斯面临生死抉择,他和他的41个同伴被敌人包围,他们决定以一种循环报数的方式决定谁将幸免于难。每数到...

    最大子段和问题的动态规划求解

    最大子段和问题,也被称为连续子数组的最大和问题,是计算机科学中一个经典的问题,尤其是在算法设计和分析领域。该问题的目的是找到一个数组中连续子数组的最大和。这个问题在许多实际应用中都有体现,比如股票投资...

    dp题库一(带答案)

    当一个问题的最优解包含其子问题的最优解时,我们称该问题具有最优子结构。而重叠子问题意味着在求解过程中会反复遇到相同的子问题,为了避免重复计算,我们可以使用存储子问题解的方法,即记忆化搜索,或者通过自底...

    动态规划法,回溯法,分支限界法求解TSP旅行商问题

    TSP旅行商问题(Traveling Salesman Problem)是一个经典的NP-hard问题,旨在找到一条最短的路径,使得旅行商可以访问每个城市一次然后返回出发点。以下是关于动态规划法、回溯法和分支限界法在TSP问题上的应用。 ...

    DP 背包问题详解pdf

    - **问题描述**:有n个物品和一个背包,背包的最大容量为V。对于每个物品i,它有一个体积v[i]和一个价值w[i]。问如何选择物品装入背包,使得背包中物品的总价值最大。 - **状态定义**:dp[i][j]表示前i个物品放入...

    DP.rar_dp算法

    动态规划(Dynamic Programming,简称DP)是计算机科学中一种强大的解决问题的方法,尤其在算法设计领域,它被广泛应用于解决最优化问题...0-1背包问题只是DP应用的一个例子,实际中还有许多其他问题可以运用DP来求解。

    汽车加油行驶问题(C语言算法设计与分析)

    在本项目中,我们主要探讨的是“汽车加油行驶问题”,这是一个经典的计算机算法问题,通常用于教学C语言的算法设计和分析。这个问题的核心是模拟一辆汽车在有限次加油的情况下,尽可能远距离行驶。以下是对该问题的...

    69、Dp入门,最短路径问题(C)-a.pdf

    6. **最优子结构**:如果一个问题的最优解包含其子问题的最优解,则该问题具有最优子结构的性质。 #### 三、代码分析 文档中给出了三种不同的实现方式: 1. **第一种实现**: - 使用了三维数组`dp`来存储不同...

    一种求解SAT问题的新方法

    可满足性问题(Satisfiability Problem, SAT)是计算科学领域中的一个经典问题,在数理逻辑、人工智能、机器学习、VLSI集成电路设计与检测、数据库检索等多个领域都有着重要的应用价值。针对这一问题,已经发展出了...

    TSP问题求解 matlab.程序t.rar

    程序t.rar"压缩包中,很可能包含了一个用MATLAB编写的解决TSP问题的程序。MATLAB提供了丰富的数学函数和工具,使得实现TSP的各种算法变得相对简单。 1. **遗传算法(Genetic Algorithm)**:遗传算法是一种模拟自然...

Global site tag (gtag.js) - Google Analytics