POJ1722 Substruct

题目:Substruct

网址:http://poj.org/problem?id=1722

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 ith contraction 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

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;
}
posted @ 2020-05-31 14:26  大秦帝国  阅读(134)  评论(0编辑  收藏  举报