题目链接

题目大意:给定\(n\)个牌,每个牌上下两部分各有一个权值,你可以将牌翻转使得上下权值交换。求在上下权值和差值绝对值最小的前提下的最小翻转次数

动态规划-背包


分析:

其实这是一道货真价实的背包题

我们要使得差值的绝对值最小,枚举一下上部分的权值和就可以了嘛

问题变成了使得上部分权值和为给定的\(w\)的最小翻转次数

\(f[i][j]\)为前\(i\)个物品使上部分权值和为\(j\)的最小翻转次数

显然\(f[i][j] = \begin{cases} f[i - 1][j - up[i]] \\ f[i - 1][j - down[i]] + 1\end{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\n",ans2);
	return 0;
} 

相关文章:

  • 2022-01-16
  • 2021-11-02
  • 2021-11-13
  • 2021-10-23
  • 2022-03-05
  • 2022-12-23
  • 2021-10-18
  • 2021-10-16
猜你喜欢
  • 2021-08-24
  • 2021-08-05
  • 2022-12-23
  • 2021-08-24
相关资源
相似解决方案