题目描述
多米诺骨牌有上下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背包题。
但是第一维维护的是第几张牌,第二维维护什么值就需要考虑一下,由于他题目每张牌最大差值只有5,且最多只有1000张牌,那,为什么差值不是5000呢?
如果你是比赛经验比较丰富的选手,你的第二维可能就会选择维护差值,dp[i][j]等于到第i张牌差值为j的最小交换次数。如果你是选择第二维维护次数,dp[i][j]等于到第i张牌交换j次的最小差值。
但是这会遇到一个问题,就是如果一个位置,交换与不交换的差值绝对值相同,那应当要存下正负两个值,下次就要4个值,最坏情况2的1000次方,但是因为单张牌最大差值只有5,最多1000张牌,最多5000*2(正负)个值,内部有很多值是相同的,由此也会得出第二维可能要维护差值而不是交换次数(但是一个dalao说这种思路可以用bitset来解决orz),代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int dp[MAXN][20*MAXN],a[MAXN],b[MAXN];
int inf, N = 5000;
int main(){
int n, i, j, k;
cin >> n;
inf = 0x7F;
for(i = 1;i <= n; i ++)
{
cin >> a[i] >> b[i];
}
memset(dp ,0x3f, sizeof(dp));
dp[0][0] = 0;
dp[0][0+N] = 0;
for(i = 1; i <= n; i ++)
{
int temp = a[i] - b[i];
for(j = i * 6; j >= -(i * 6); j --)
{
dp[i][j+N] = min(dp[i-1][j+temp+N], dp[i-1][j-temp+N]+1);
}
}
int ans;
for(i = 0; i <= n * 6 ; i ++)
{
ans = min(dp[n][i+N], dp[n][-i+N]);
if(ans < 1000)
break;
}
cout<< ans;
return 0;
}