思路:背包+逆推。
很明显可以发现:
t
=
a
1
−
a
2
+
∑
i
=
3
n
±
a
i
t=a_1-a_2+\sum\limits_{i=3}^n\pm a_i
t=a1−a2+i=3∑n±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]表示前i个数的和为j,且第i个数的符号为f[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……=−(a−b−c−d−e−……)(负负得正)
所以,我们可以先把连续正数及其前面的那一个数合并起来,最后减去剩余的数即可。
具体的,顺序扫描数组,
若
第
i
个
数
为
正
数
,
且
s
为
前
面
所
作
的
减
操
作
次
数
,
则
当
前
的
操
作
位
置
为
i
−
(
s
+
+
)
−
1
,
即
i
−
(
+
+
s
)
若第i个数为正数,且s为前面所作的减操作次数,则当前的操作位置为i-(s++)-1,即i-(++s)
若第i个数为正数,且s为前面所作的减操作次数,则当前的操作位置为i−(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;
}