转自http://hi.baidu.com/flabbyan/item/5383903d55235f647d034b70
题目大意:给成一组多米诺牌,每个多米诺牌由上面和下面两组数组成,现要求可以翻动颠倒上下,使得多米诺上边的点数和减去下边的点数和的绝对值最小
解题思路:
之前想的一个。。。。
用一个dp[i[0]表示第i个没颠倒的前i个牌和之差,dp[i][0]= dp[i-1][1] + (a - b) 当abs(dp[i-1][1] + (a-b) )< abs(dp[i-1][0] + (a-b)) 反之为dp[i-1][0] + (a - b)
dp[i][1]表示第i个上下翻转了的前j个牌之差。
然后用个record[i][0],record[i][1]记录翻转次数
是错的,不能保证最优子结构!!!!!!!
看了别人的解答。。。思路是这样的
dp[k] 表示多米诺前x个上下数之和为k时的最小翻动次数。
那么当处理第i个牌时
dp[k + i.a] = min(dp[k], dp[k+i.a])
dp[k+ i.b] = min(dp[k] + 1, dp[k+i.b])
每次推算时得记得重置dp[k]。
最后,从总和的一半开始往前,往后各找到一个状态,找出这两个状态的最优的就是所求。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
const int maxn = 12005;
const int inf = 0x7fffffff;
int dp[maxn], n;
int main()
{
memset(dp, -1, sizeof(dp));
dp[0] = 0;
int s = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
int a, b;
scanf("%d %d", &a, &b);
s += (a + b);
for(int k = s; k >= 0; k--)
{
int tmp;
if(dp[k] != -1)
{
tmp = dp[k];
dp[k] = -1; //记得要重置,为下次推算做准备
if(dp[k + a] == -1 || dp[k + a] > tmp)
dp[k + a] = tmp;
if(dp[k + b] == -1 || dp[k + b] > tmp + 1)
dp[k + b] = tmp + 1;
}
}
}
s /= 2;
int i, j;
for(i = s; i >= 0; i--)
{
if(dp[i] != -1)
break;
}
for(j = s; j < maxn; j++)
{
if(dp[j] != -1)
break;
}
printf("%d\n", dp[i] > dp[j] ? dp[j] : dp[i]);
return 0;
}