POJ 1722 SUBTRACT

在这里插入图片描述

思路:背包+逆推。

很明显可以发现:
t = a 1 − a 2 + ∑ i = 3 n ± a i t=a_1-a_2+\sum\limits_{i=3}^n\pm a_i t=a1a2+i=3n±ai
所以我们定义背包数组 f [ i ] [ j ] 表 示 前 i 个 数 的 和 为 j , 且 第 i 个 数 的 符 号 为 f [ i ] [ j ] ( 1 为 正 , − 1 为 负 ) f[i][j]表示前i个数的和为j,且第i个数的符号为f[i][j](1为正,-1为负) f[i][j]ijif[i][j](1,1)

这道题的难点是逆推操作位置。
以下正负数都是指添了 ± \pm ±号之后的数
可以发现,第3个数以后的一段连续的正数前面一定是一个负数。
形如: − a + b + c + d + e … … -a+b+c+d+e…… a+b+c+d+e(字母均表示正数)
则有: − a + b + c + d + e … … = − ( a − b − c − d − e − … … ) -a+b+c+d+e……=-(a-b-c-d-e-……) a+b+c+d+e=(abcde)(负负得正)
所以,我们可以先把连续正数及其前面的那一个数合并起来,最后减去剩余的数即可。
具体的,顺序扫描数组, 若 第 i 个 数 为 正 数 , 且 s 为 前 面 所 作 的 减 操 作 次 数 , 则 当 前 的 操 作 位 置 为 i − ( s + + ) − 1 , 即 i − ( + + s ) 若第i个数为正数,且s为前面所作的减操作次数,则当前的操作位置为i-(s++)-1,即i-(++s) isi(s++)1,i(++s)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,X=10000;
int n,m,s;
char f[N][2*X+5],o[N],a[N];
int main() {
	scanf("%d %d",&n,&m); m+=X;
	for(int i=1,x;i<=n;i++)scanf("%d",&x),a[i]=x;
	f[2][a[1]-a[2]+X]=-1;s=abs(a[1]-a[2]);
	for(int i=3;i<=n;s+=a[i++])
		for(int j=-s;j<=s;j++)
			if(f[i-1][j+X]) {
				f[i][j+a[i]+X]=1;
				f[i][j-a[i]+X]=-1;
			}
	for(int i=n;i>1;i--)
		m-=(o[i]=f[i][m])*a[i];
	s=0;
	for(int i=3;i<=n;i++)
		if(o[i]==1)
			printf("%d\n",i-++s);
	while(++s<n)puts("1");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值