`
caoruntao
  • 浏览: 467821 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

动态规划:ZOJ2042_Divisibility

 
阅读更多

【转】http://fuliang.iteye.com/blog/143188

 

问题: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)了:
代码如下:

package com.iteye.caoruntao.zoj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author caoruntao
 *
 */
public class ZOJ2042_Divisibility {

	private static int N;
	private static int K;
	private static int[][] flag = new int[10005][105];
	private static int[] a = new int[10005];
	
	public static int compute(int level, int value){
		if(flag[level][value] != -1){
			return flag[level][value];
		}
		int check;
		int t1 = (value + a[level]) % K;
		int t2 = (value - a[level]) % K;
		t1 = t1 < 0 ? t1 + K : t1;
		t2 = t2 < 0 ? t2 + K : t2;
		
		if(level == N){
			if(value%K == 0){
				check = 1;
			}else{
				check = 0;
			}
		}
		else if(compute(level+1, t1) == 1){
			check = 1;
		}
		else if(compute(level+1, t2) == 1){
			check = 1;
		}
		else{
			check = 0;
		}
		return flag[level][value]=check;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int n;
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = "";
		String result = "";
		try {
			str = br.readLine();
			n = Integer.parseInt(str);
			for(int i=0; i<n; i++){
				str = br.readLine();
				String[] strArr = str.split(" ");
				N = Integer.parseInt(strArr[0]);
				K = Integer.parseInt(strArr[1]);
				str = br.readLine();
				strArr = str.split(" ");
				for(int j=0; j<N; j++){
					a[j] = Integer.parseInt(strArr[j]);
				}
				
				for(int j=0; j<=N; j++){
					for(int m=0; m<=K; m++){
						flag[j][m] = -1;
					}
				}
				result = compute(1, a[0]%K)==1 ? "Divisible" : "Not divisible";
				System.out.println(result);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics