题目链接:https://www.luogu.org/problemnew/show/P1282
花了好长时间终于写出了这道题,主要是状态转移方程比较奇葩,类似于背包问题,难以想到。
呃呃,写得DP不多,对于如何想出状态转移方程还没什么心得,主要是往之前见过的模型上靠。这道题的话,其实可以稍微转化一下设问,变成有n个数,每次操作可以将其变为相反数,问最少操作几次可以使总和的绝对值最小。设dp[i][j]为考虑到第i个数,总和为j时最少的操作次数。状态转移方程为dp[i][j]=min(dp[i-1][j-num[i]],dp[i-1][j+num[i]]+1)。因为是取最小值,一开始要把dp数组初始化成inf,还要将未进行操作的dp初始化为0。反正代码细节也还是有的。
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 6 using namespace std; 7 8 const int maxn = 1e3 + 5, maxm = 1e4 + 5, inf = 0x3f3f3f3f; 9 10 int num[maxn], dp[maxn][maxm], mm[maxn]; 11 12 inline int map(int x) { 13 return x + 5e3 + 1; 14 } 15 16 int main() { 17 int n, a, b, sum = 0; 18 scanf("%d", &n); 19 for (int i = 1; i <= n; ++i) { 20 scanf("%d%d", &a, &b); 21 num[i] = a - b; 22 } 23 memset(dp, inf, sizeof(dp)); 24 for (int i = 0; i <= n; ++i) { 25 dp[i][map(sum)] = 0; 26 sum += num[i]; 27 mm[i] = mm[i - 1] + abs(num[i]); 28 } 29 for (int i = 1; i <= n; ++i) 30 for (int j = -mm[i]; j <= mm[i]; ++j) 31 dp[i][map(j)] = min(dp[i - 1][map(j - num[i])], dp[i - 1][map(j + num[i])] + 1); 32 for (int i = 0; i <= mm[n]; ++i) 33 if (dp[n][map(i)] != inf || dp[n][map(-i)] != inf) { 34 printf("%d", min(dp[n][map(i)],dp[n][map(-i)])); 35 return 0; 36 } 37 return 0; 38 }