参考:
https://blog.csdn.net/qq_51354600/article/details/120623720
题意
给定\(n\)个多米诺骨牌,每个多米诺骨牌由上下两部分组成,每部分的点数为\(1 \sim 6\)中的某一个数且已给定。
记上下\(2\)行点数之差为上部分的点数之和-下部分的点数之和。
每一个多米诺骨牌可以执行操作使得上下交换,求最小交换次数使得上下\(2\)行点数最小
题解
求最小点数的最小交换次数->求每个点数下的最小交换次数
令\(dp[i][j]\)表示前\(i\)个多米诺骨牌,上部分的点数和为\(j\)下的最小交换次数。假设\(sum\)为上下两部分的点数总和,那么点数之差可以表示为\(|j-(sum-j)|\)。
预处理每个\(j\)下的最小交换次数,类似背包问题,要么选择上部分点数以加入,要么交换一次以加入下部分点数。
枚举每个\(j\),优先更新最小的点数差,如果点数差相等则更新最小交换次数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
const int maxn = 1000 + 10;
const int inf = 0x3f3f3f3f;
int n;
int sum;
int a[maxn], b[maxn];
int dp[maxn][6 * maxn];
int res = 0, minus = inf;
signed main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i], &b[i]);
sum += a[i] + b[i];
}
memset(dp, inf, sizeof(dp));
dp[0][0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 6 * i; j++) {
if (j >= a[i]) dp[i][j] = std::min(dp[i][j], dp[i - 1][j - a[i]]);
if (j >= b[i]) dp[i][j] = std::min(dp[i][j], dp[i - 1][j - b[i]] + 1);
}
for (int i = n; i <= 6 * n; i++) {
int temp = abs(i - (sum - i));
if (temp < minus) {
minus = temp;//更新最小差值
res = dp[n][i];
} else if (temp == minus) res = std::min(res, dp[n][i]);
}
printf("%d", res);
return 0;
}
标签:洛谷,多米诺骨牌,int,最小,maxn,P1282,点数,dp
From: https://www.cnblogs.com/MrWangnacl/p/17036629.html