洛谷 P1282 多米诺骨牌 01背包

传送门

题目描述
多米诺骨牌有上下2个方块组成,每个方块中有1~6个点。现有排成行的

上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|。例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。

每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置。 编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小。
在这里插入图片描述
对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0。

输入格式
输入文件的第一行是一个正整数n(1≤n≤1000),表示多米诺骨牌数。接下来的n行表示n个多米诺骨牌的点数。每行有两个用空格隔开的正整数,表示多米诺骨牌上下方块中的点数a和b,且1≤a,b≤6。

输出格式
输出文件仅一行,包含一个整数。表示求得的最小旋转次数。

输入输出样例

思路

对于有多个物体,每个物体只有两个状态的,一般都是01背包了

定义状态
dp[i][j] 表示0-i下标号骨牌能够形成上下差值为j的局面,需要的最少移动次数

注意:
因为这里骨牌的差值可能是负的,所以用数组存的话,加一个偏移量就行,最大值不超过6000,那么我使用6009作为偏移,即0下标表示差值为-6009,6009下标表示差值为0

状态转移:
设上面的数字用n1[]数组存储,下面的用n2[]数组
对于第i块骨牌,如果当前局面的差值是j

  • 如果骨牌正着放,那么0~i-1下标会形成差值为 j-n1[i]+n2[i]的局面
  • 如果骨牌倒着放,那么0~i-1下标会形成差值为 j-n2[i]+n1[i]的局面,问题答案为上述局面答案+1
  • 这一块骨牌是否倒放,取决于从上述两个局面,形成差值为j的局面,哪一个需要反转的次数少
 dp[i][j] = min(dp[i-1][j-n1[i]-n2[i]], dp[i-1][j-n2[i]-n1[i]]+1)

因为枚举的是差值(价值),计算完dp数组之后,那么从0往两边找差值最小的,且移动次数最小的即可

代码

#include <bits/stdc++.h>

using namespace std;

int n1[1009], n2[1009];
int n;
int dp[1009][6009*2];

int main()
{	
	cin>>n; 
	for(int i=0; i<n; i++) cin>>n1[i]>>n2[i];
	for(int i=0; i<1009; i++)
		for(int j=0; j<6009*2; j++) dp[i][j]=1145141919;
		
	for(int i=0; i<6009*2; i++)
		if(n1[0]-n2[0]+6009==i) dp[0][i]=0;
		else if(n2[0]-n1[0]+6009==i) dp[0][i]=1;
		
	for(int i=1; i<n; i++)
		for(int j=0; j<6009*2; j++)
			if(j-n1[i]+n2[i]>=0 && j-n2[i]+n1[i]>=0)
				dp[i][j] = min(dp[i-1][j-n2[i]+n1[i]]+1, dp[i-1][j-n1[i]+n2[i]]);
	
	int i=6009, j=6009;
	while(i>=0 && j<6009*2)
	{
		if(dp[n-1][i]<=n || dp[n-1][j]<=n)
		{
			cout<<min(dp[n-1][i], dp[n-1][j])<<endl; 
			break;
		}
		i--; j++;
	}
	
	return 0;
}
发布了238 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104972903