问题重述:
给定一个数组a[1,2,..,n] 。定义数组第i位上的减操作:把ai和ai+1换成ai - ai+1。输入一个n位数组以及目标整数t,求一个n-1次操作序列,使得最后剩下的数等于t。
分析:
显然最后剩下的整数是在初始数组中各个元素前添加正负号后相加得到的结果,其中a1的符号必为正,a2必为负。可以通过动态规划确定每个元素的符号。
令dp[i][j]表示从前往后计算到第i个数时结果为j的a[i]前的符号:dp[i][j] = 1表示a[i]取正号;dp[i][j]=-1表示a[i]取负号;dp[i][j] = 0表示当前结果无法取到j。那么假如dp[i-1][j] != 0, 则有dp[i][j + a[i]] = 1, dp[i][j -a[i]] = -1。最后可以通过dp[n][t]倒序计算出每个元素前的符号。
取正号的元素可以看成进行了两次减操作。因此,可以先从左到右对取正号的元素进行一次减操作,再从左到右对每个元素进行减操作即可得到结果。
AC代码
1 //Memory: 8364K Time: 63MS 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 6 using namespace std; 7 8 const int maxn = 105; 9 const int maxt = 20010; 10 const int offset = 10005; 11 int n, t; 12 int a[maxn]; 13 int dp[maxn][maxt]; 14 int ans[maxn]; 15 16 void dynamic() 17 { 18 memset(dp, 0, sizeof(dp)); 19 dp[1][a[1] + offset] = 1; 20 dp[2][a[1] - a[2] + offset] = -1; 21 for (int i = 3; i <= n; i++) { 22 for (int j = -10000 + offset; j <= 10000 + offset; j++) { 23 if (dp[i - 1][j] != 0) { 24 dp[i][j + a[i]] = 1; 25 dp[i][j - a[i]] = -1; 26 } 27 } 28 } 29 } 30 31 void output() 32 { 33 memset(ans, 0, sizeof(ans)); 34 int s = t + offset; 35 for (int i = n; i >= 1; i--) { 36 ans[i] = dp[i][s]; 37 s -= ans[i] * a[i]; 38 } 39 int cnt = 0; 40 for (int i = 2; i <= n; i++) { 41 if (ans[i] == 1) { 42 printf("%d\n", i - cnt - 1); 43 cnt++; 44 } 45 } 46 for (; cnt < n - 1; cnt++) 47 printf("1\n"); 48 } 49 50 int main() 51 { 52 scanf("%d%d", &n, &t); 53 for (int i = 1; i <= n; i++) { 54 scanf("%d", &a[i]); 55 } 56 dynamic(); 57 output(); 58 return 0; 59 }
时间: 2024-10-29 12:53:46