题目描述
多米诺骨牌有上下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。
输出格式:
输出文件仅一行,包含一个整数。表示求得的最小旋转次数。
输入输出样例
输入样例#1:复制
4
6 1
1 5
1 3
1 2
输出样例#1:复制
1
题目分析:
DP还是理解不够,背包还是没得心运手啊。
可以转化为01背包问题,只不过我们必须要选,该题的上面a[i]和下面b[i]差值的累积和不知道,但我们知道最大为5000(题目暗含),好,我们把这5000看成背包的容量,
但,我们不要忘了累加差值还有负数,所以就扩大为-5000~5000,但数组下标不能为负数,所以,定义个常数5000即可。
则我们定义f[i][j]为前i组数最少交换次数得到最小差值累加和j
状态转移方程:f[i][j+N]=min(f[i-1][j+(a[i]-b[i])+N],f[i-1][j-(a[i]-b[i])+N])
别忘了初始化f[0][N+0]=0
代码实现:
#include<bits/stdc++.h>
using namespace std;
int f[1005][13002];
const int N=5000; //差值最大5000,因为数组下标不能为负数,所以加一个最大值
int main()
{int n,a[1005],b[1005];int i,j;cin>>n;for(i=1;i<=n;i++)cin>>a[i]>>b[i];memset(f,127,sizeof(f));f[0][N+0]=0;for(i=1;i<=n;i++) //枚举前i个for(j=-5000;j<=5000;j++) //枚举可能加到的和{int dis=a[i]-b[i];f[i][j+N]=min(f[i-1][j+dis+N],f[i-1][j-dis+N]+1);//换与不换,但必须选}int minn;for(i=0;i<=5000;i++){minn=min(f[n][i+N],f[n][N-i]);//不知道累加的差和具体在哪里,慢慢查找if(minn<=1000) //最多调换1000次{cout<<minn;break;}}return 0;
}