网址:http://poj.org/problem?id=1722
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.
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 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 ith contraction to be applied.
You can assume that at least one such sequence of contractions will exist for a given input.
5 4
12
10
4
3
5
2
3
2
1
CEOI 1998
这题命题风格有点像codeforces
经过观察发现,在a1~an中,a1以及a2的正负号是一定的,相反,其他的是任意的。(换句话说,题目要求:a1 - a2 +/- a3 +/- … +/- an = t);
定义状态:dp[i, j]代表考虑第i位左端正负号,使得ai ~ an的和为j的可行性。(换句话讲,通过改变第i至n个数左侧正负号使得总和为j)
不难想出转移方程:dp[i, j] = dp[i + 1, j + a[i]] | dp[i + 1, j - a[i]],dp[i, j] = 其中,true指该状态可行,false为不可行。
但是由于j可能是负的,所以我们可以将t加上一个比较大的常数,使状态便于表示。
本题核心在于如何寻找完整的操作顺序。
由于总共n - 1个操作,真正影响序列结果的是顺序。我们先通过上述的DP状态处理每个数左端的正负号。
我们考虑:若第i个数前符号为负号,那么它先进行操作一定是合理的。(此中有真意,欲辨已忘言,还请列位感受感受)
由此我们先把左端符号为正号的数进行操作,接着从左到右将其余的数操作。对于前者,我们需要记录一个变量tot,用以记录已经进行操作次数。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 100 + 10;
const int SIZE = 40000 + 5;
vector <int> sub;
int n, t, a[maxn], tot = 0;
bool dp[maxn][SIZE];
void search_route(int cur, int sum)
{
if(cur > n) return;
if(dp[cur + 1][sum + a[cur]])
{
sub.push_back(0);
search_route(cur + 1, sum + a[cur]);
}
else if(dp[cur + 1][sum - a[cur]])
{
sub.push_back(1);
search_route(cur + 1, sum - a[cur]);
}
return;
}
int main()
{
scanf("%d %d", &n, &t);
t += 20000;
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
t -= a[1] - a[2];
memset(dp, false, sizeof(dp));
dp[n + 1][20000] = true;
for(int i = n; i >= 3; -- i)
{
for(int j = a[i]; j <= 40000 - a[i]; ++ j)
{
dp[i][j] = dp[i + 1][j - a[i]] | dp[i + 1][j + a[i]];
}
}
sub.clear();
sub.push_back(1), sub.push_back(0);
search_route(3, t);
for(int i = 1; i < sub.size(); ++ i)
{
if(sub[i] == 1)
{
printf("%d\n", i - tot);
++ tot;
}
}
for(int i = 1; i < sub.size(); ++ i)
{
if(sub[i] == 0)
{
printf("%d\n", 1);
}
}
return 0;
}
原文:https://www.cnblogs.com/zach20040914/p/13004691.html