poj1722 SUBTRACT【线性DP】

SUBTRACT
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 2037   Accepted: 901   Special Judge

Description

We are given a sequence of N positive integers a = [a1, a2, ..., aN] on which we can perform contraction operations. 
One contraction operation consists of replacing adjacent elements ai and ai+1 by their difference ai-ai+1. For a sequence of N integers, we can perform exactly N-1 different contraction operations, each of which results in a new (N-1) element sequence. 

Precisely, let con(a,i) denote the (N-1) element sequence obtained from [a1, a2, ..., aN] by replacing the elements ai and ai+1 by a single integer ai-ai+1 : 

con(a,i) = [a1, ..., ai-1, ai-ai+1, ai+2, ..., aN] 
Applying N-1 contractions to any given sequence of N integers obviously yields a single integer. 
For example, applying contractions 2, 3, 2 and 1 in that order to the sequence [12,10,4,3,5] yields 4, since : 
con([12,10,4,3,5],2) = [12,6,3,5]

con([12,6,3,5] ,3) = [12,6,-2]
con([12,6,-2] ,2) = [12,8]
con([12,8] ,1) = [4]

Given a sequence a1, a2, ..., aN and a target number T, the problem is to find a sequence of N-1 contractions that applied to the original sequence yields T.

Input

The first line of the input contains two integers separated by blank character : the integer N, 1 <= N <= 100, the number of integers in the original sequence, and the target integer T, -10000 <= T <= 10000. 
The following N lines contain the starting sequence : for each i, 1 <= i <= N, the (i+1)st line of the input file contains integer ai, 1 <= ai <= 100. 

Output

Output should contain N-1 lines, describing a sequence of contractions that transforms the original sequence into a single element sequence containing only number T. The ith line of the output file should contain a single integer denoting the ithcontraction to be applied. 
You can assume that at least one such sequence of contractions will exist for a given input. 

Sample Input

5 4
12
10
4
3
5

Sample Output

2
3
2
1

Source

题意:

给N个数,每次选定一个i,用a[i] - a[i+1]的结果取代a[i]和a[i+1]。操作N-1次之后,就只剩下1个数。问如何选择,可以使这个数恰好是T

思路:

我们可以发现,这个序列每次用减来取代的话,其实就是在n个数之前加上正负,然后让他们都加起来的和结果是T。

而且a[1]一定是正,a[2]一定是负。

于是我们用dp[i][j]来表示第i个数得到分数j时的符号,1表示是加,-1表示是减,0表示没有扩展到。【我怎样才能聪明地想到要这样来表示呢】

我们枚举i,j,判断上一个状态i-1有没有扩展到当前分数j。如果有,那么dp[i][j + a[i]] = 1, dp[i][j - a[i]] = -1

然后我们从dp[n][t]开始倒推出每一个元素之前的符号。ans[i]表示的就是a[i]前的符号。

那么方案该怎么输出?

先去处理所有的加号,相当于把所有的加号都挪去了原来a3的位置,这样最后处理减号的时候,减a2就可以负负得正了。

然后处理减号,把所有的减号都挪到了原来a2的位置,给a1来减。

 1 //#include <bits/stdc++.h>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<stdio.h>
 6 #include<cstring>
 7 #include<vector>
 8 #include<map>
 9 #include<set>
10 
11 #define inf 0x3f3f3f3f
12 using namespace std;
13 typedef long long LL;
14 
15 int n, t;
16 int a[105];
17 int dp[105][20010], ans[105];
18 const int base = 10005;
19 
20 int main()
21 {
22     while(scanf("%d%d", &n, &t) != EOF){
23         for(int i = 1; i <= n; i++){
24             scanf("%d", &a[i]);
25         }
26         memset(dp, 0, sizeof(dp));
27         dp[1][a[1] + base] = 1;
28         dp[2][a[1] - a[2] + base] = -1;
29         for(int i = 3; i <= n; i++){
30             for(int j = -10000 + base; j <= 10000 + base; j++){
31                 if(dp[i - 1][j] != 0){
32                     dp[i][j + a[i]] = 1;
33                     dp[i][j - a[i]] = -1;
34                 }
35             }
36         }
37 
38         int want = t + base;
39         for(int i = n; i >= 2; i--){
40             ans[i] = dp[i][want];
41             if(ans[i] == 1){
42                 want -= a[i];
43             }
44             else if(ans[i] == -1){
45                 want += a[i];
46             }
47         }
48 
49         int cnt = 0;
50         for(int i = 2; i <= n; i++){
51             if(ans[i] == 1){
52                 printf("%d
", i - cnt - 1);
53                 cnt++;
54             }
55         }
56         for(int i = 2; i <= n; i++){
57             if(ans[i] == -1){
58                 printf("1
");
59             }
60         }
61     }
62     return 0;
63 }