题目链接:https://www.luogu.com.cn/problem/P1282
每个骨牌都有翻转或不翻转两种状态,体积分别为 (a[i]-b[i]) 和 (b[i]-a[i]) 可以为负,代价分别为 (0) 和 (1),最终要求使体积绝对值最小的代价最小
设 (dp[i][j]) 表示前 (i) 个骨牌,体积为 (j) 时的最小代价,(01) 背包转移即可,注意负坐标要偏移
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1010;
const int INF = 0x3f3f3f3f;
int n;
int a[maxn], b[maxn];
int dp[maxn][30*maxn];
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
n = read();
for(int i = 1 ; i <= n ; ++i) a[i] = read(), b[i] = read();
memset(dp, 0x3f, sizeof(dp));
dp[0][0+7000] = 0;
for(int i = 1 ; i <= n ; ++i){
for(int j = 7000 ; j >= -7000 ; --j){
if(dp[i-1][j-(a[i]-b[i])+7000] != INF) dp[i][j+7000] = min(dp[i][j+7000], dp[i-1][j-(a[i]-b[i])+7000]); // 不转
if(dp[i-1][j-(b[i]-a[i])+7000] != INF) dp[i][j+7000] = min(dp[i][j+7000], dp[i-1][j-(b[i]-a[i])+7000] + 1); // 转
}
}
int ans = INF; int flag = 0;
for(int i = 0 ; i <= 6000 ; ++i){
if(dp[n][i+7000] != INF) {
ans = min(ans, dp[n][i+7000]);
flag = 1;
}
if(dp[n][-i+7000] != INF) {
ans = min(ans, dp[n][-i+7000]);
flag = 1;
break;
}
if(flag) break;
}
printf("%d
", ans);
return 0;
}