Solution 多米诺骨牌
题目大意:给定(n)个牌,每个牌上下两部分各有一个权值,你可以将牌翻转使得上下权值交换。求在上下权值和差值绝对值最小的前提下的最小翻转次数
动态规划-背包
分析:
其实这是一道货真价实的背包题
我们要使得差值的绝对值最小,枚举一下上部分的权值和就可以了嘛
问题变成了使得上部分权值和为给定的(w)的最小翻转次数
设(f[i][j])为前(i)个物品使上部分权值和为(j)的最小翻转次数
显然(f[i][j] = egin{cases} f[i - 1][j - up[i]] \ f[i - 1][j - down[i]] + 1end{cases}),其中(up,down)分别表示一张牌上下部分的权值
然后枚举一下,注意翻过了的情况,也就是说假设翻了(k)次,但是(n-k<k)的话我们实际上是只用翻(n-k)次的……
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1024;
int val[maxn][2],f[maxn][6666],n,mxsum,sum,ans1,ans2;
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++)
scanf("%d %d",&val[i][0],&val[i][1]),mxsum += max(val[i][0],val[i][1]),sum += val[i][0] + val[i][1];
memset(f,0x3f,sizeof(f));
f[0][0] = 0;
for(int i = 1;i <= n;i++)
for(int j = 0;j <= mxsum;j++){
if(j >= val[i][0])f[i][j] = min(f[i][j],f[i - 1][j - val[i][0]]);
if(j >= val[i][1])f[i][j] = min(f[i][j],f[i - 1][j - val[i][1]] + 1);
}
ans1 = 0x7fffffff;
for(int now = 0;now <= mxsum;now++)
if(f[n][now] <= n && (abs(sum - now * 2) < ans1 || (abs(sum - now * 2) == ans1 && min(f[n][now],n - f[n][now]) < ans2)))ans1 = abs(sum - now * 2),ans2 = min(f[n][now],n - f[n][now]);
printf("%d
",ans2);
return 0;
}