题意:一些骨牌,有上下两部分,都可以旋转,现在要旋转一些骨牌使得上下之差绝对值最小下的最小旋转步骤
思路:DP,每块骨牌相当于背包的一个物品,然后dp[i][j]表示i块骨牌,差为j的最小旋转次数
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 12005;
const int INF = 0x3f3f3f3f;
int n, dp[2][N], have[N], hn, to[N];
int a[1005], b[1005];
int main() {
while (~scanf("%d", &n)) {
int sum = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d", &a[i], &b[i]);
sum = sum + a[i] - b[i];
}
int now = 0, pre = 1;
memset(dp[now], INF, sizeof(dp[now]));
dp[now][6000 + sum] = 0;
hn = 0;
have[hn++] = 6000 + sum;
memset(to, -1, sizeof(to));
to[6000 + sum] = 0;
for (int i = 0; i < n; i++) {
swap(now, pre);
memset(dp[now], INF, sizeof(dp[now]));
for (int j = hn - 1; j >= 0; j--) {
int tmp = have[j] - a[i] * 2 + b[i] * 2;
if (to[tmp] == -1) {
to[tmp] = hn;
have[hn++] = tmp;
}
dp[now][tmp] = min(dp[now][tmp], dp[pre][have[j]] + 1);
dp[now][have[j]] = min(dp[now][have[j]], dp[pre][have[j]]);
}
}
int ansv = 0;
for (int i = 0; i < hn; i++) {
if (abs(have[i] - 6000) < abs(have[ansv] - 6000)) {
ansv = i;
} else if (abs(have[i] - 6000) == abs(have[ansv] - 6000) && dp[now][have[i]] < dp[now][have[ansv]]) {
ansv = i;
}
}
printf("%d\n", dp[now][have[ansv]]);
}
return 0;
}