题意:给出$N$个在$-5$到$5$之间的数字,你可以对任意一个数字取相反数,问最少取多少次相反数可以使得它们的和在所有方案中最接近$0$。$N \leq 10^4$
拔河型01背包
这种背包中物品的取法有变化:一种是取正值,一种是取负值。我们仍然可以像原来的$01$背包一样进行转移,但是因为转移的方向不是固定的,所以不能像原来的一维数组DP一样进行DP,需要一个辅助数组装新转移出来的状态。
1 #include<bits/stdc++.h>
2 using namespace std;
3 int num[10001] , numa[10001];
4 inline int fast_read()
5 {
6 char ch = getchar();
7 int a = 0;
8 while(!isdigit(ch)) ch = getchar();
9 while(isdigit(ch)) a = a * 10 + ch - '0' , ch = getchar();
10 return a;
11 }
12 int main()
13 {
14 int maxC = 0;
15 memset(num , -1 , sizeof(num));
16 num[5000] = 0;
17 for(int n = fast_read() ; n ; n--)
18 {
19 int a = fast_read() , b = fast_read();
20 for(int i = 5000 - maxC - (int)abs(a - b) ; i <= 5000 + maxC + (int)abs(a - b) ; i++)
21 numa[i] = -1;
22 for(int i = 5000 - maxC ; i <= 5000 + maxC ; i++)
23 if(num[i] != -1)
24 {
25 numa[i + a - b] = numa[i + a - b] == -1 ? num[i] : min(num[i] , numa[i + a - b] );
26 numa[i - a + b] = numa[i - a + b] == -1 ? num[i] + 1 : min(num[i] + 1 , numa[i - a + b]);
27 }
28 maxC += (int)abs(a - b);
29 for(int i = 5000 - maxC ; i <= 5000 + maxC ; i++)
30 num[i] = numa[i];
31 }
32 for(int dirA = 5000 , dirB = 5000 ; ; dirA++ , dirB--)
33 if(num[dirA] != -1 || num[dirB] != -1)
34 {
35 if(num[dirA] == -1) cout << num[dirB];
36 else if(num[dirB] == -1) cout << num[dirA];
37 else cout << min(num[dirB] , num[dirA]);
38 break;
39 }
40 return 0;
41 }
五个月前我竟是画风清奇的压行+大括号换行党