题意:一个长为 N (1 <= N <= 100)的序列(1 <= ai <= 100),一次操作为删除 a[i] 和 a[i + 1],然后将它们的差(a[i] - a[i + 1])放入该位置,问 N - 1 次操作后得到 T (-10000 <= T <= 10000)的操作顺序是什么?
题目链接:http://poj.org/problem?id=1722
——>>每次操作相当于给 a[i] 和 a[i + 1] 加括号做减法,那么把所有的括号去掉后就是对序列第一次做减法,后面或加法或减法。。
状态:dp[i][j] 表示前 i 个数的运算结果为 j 时最后一次的运算符号。。("+" 或 "-")
状态转移方程:
dp[i + 1][j - a[i + 1]] = '-';
dp[i + 1][j + a[i + 1]] = '+';
#include <cstdio>
#include <cstring>
const int MAXN = 100 + 10;
const int MAXT = 20000 + 10;
const int MID = 10000;
int N, T;
int a[MAXN];
char dp[MAXN][MAXT];
char op[MAXN];
void Read()
{
for (int i = 1; i <= N; ++i)
{
scanf("%d", a + i);
}
}
void Dp()
{
if (N == 1) return;
memset(dp, 0, sizeof(dp));
dp[2][a[1] - a[2] + MID] = '-';
for (int i = 2; i < N; ++i)
{
for (int j = 0; j < (MID << 1); ++j)
{
if (dp[i][j] != 0)
{
dp[i + 1][j - a[i + 1]] = '-';
dp[i + 1][j + a[i + 1]] = '+';
}
}
}
}
void GetPath()
{
int t = T + MID;
for (int i = N; i >= 2; --i)
{
op[i] = dp[i][t];
if (op[i] == '+')
{
t -= a[i];
}
else
{
t += a[i];
}
}
}
void Output()
{
int done = 0;
for (int i = 2; i <= N; ++i)
{
if (op[i] == '+')
{
printf("%d\n", i - 1 - done);
++done;
}
}
for (int i = 2; i <= N; ++i)
{
if (op[i] == '-')
{
puts("1");
}
}
}
int main()
{
while (scanf("%d%d", &N, &T) == 2)
{
Read();
Dp();
GetPath();
Output();
}
return 0;
}