【题解】多米诺骨牌

题目描述

        有一种多米诺骨牌是平面的,其正面被分成上下两个部分,每一部分的表面或者为空,或者被标上1至6个点。现在有一行多米诺骨牌排列在桌面上:

Failed to load picture

        顶行(上行)骨牌的点数之和为6+1+1+1=9;底行(下行)骨牌的点数之和为1+5+3+2=11。顶行和底行的差值为2,这个差值是上下两行点数之和的差的绝对值。每个多米诺骨牌都可以上下翻转交换,即上部变为下部,下部变为上部。

        现在的任务是,以最少的翻转次数,使得顶行和底行之间的差值最小。对于上面这个例子,我们只需要翻转最后一个骨牌,就可以使得顶行和底行的差值为0。所以这个例子的答案为1。

输入格式

        第一行是一个整数n(1≤n≤1000),表示有n个多米诺骨牌在桌面上排成一行。

        接下来总共有n行,每行包含两个整数a、b(0≤a,b≤6,中间用空格分开)。第i+1行的a、b分别表示第i个多米诺骨牌的上部和下部的点数(0表示空)。

输出格式

        只有一个整数,这个整数表示翻动骨牌的最少次数,从而使得顶行和底行的差值最小。

 

输入样例

4

6 1

1 5

1 3

1 2

输出样例

1

题解

         我们只需要记录当前顶部数字和为$i$的最小翻转次数即可,这里可以用滚动数组优化。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define MAXN 1001

using namespace std;

int n;
int a[MAXN], b[MAXN];
int sum;
int maxa;
int f[6001];// 当顶部数字和为i时需要最少翻转几个骨牌
int ans;

int main()
{
    scanf("%d", &n);
    for(register int i = 1; i <= n; i++)
    {
        scanf("%d%d", &a[i], &b[i]);
        sum += a[i] + b[i];
        maxa += a[i];
    }
    for(register int i = 0; i <= 6000; i++) f[i] = -1;
    f[maxa] = 0;
    for(register int i = 1; i <= n; i++)
    {
        if(a[i] < b[i]) maxa += b[i] - a[i];
        if(a[i] >= b[i]) for(register int j = 0; j <= maxa - a[i] + b[i]; j++) 
        {
            if(f[j - b[i] + a[i]] != -1) f[j] = min((f[j] == -1 ? n + 1 : f[j]), f[j - b[i] + a[i]] + 1);
        }
        else for(register int j = maxa; j >= b[i] - a[i]; j--)
        {
            if(f[j - b[i] + a[i]] != -1) f[j] = min((f[j] == -1 ? n + 1 : f[j]), f[j - b[i] + a[i]] + 1);
        }
    }
    for(register int i = 1; i <= maxa; i++) if(abs(sum - i * 2) < abs(sum - ans * 2) || abs(sum - i * 2) == abs(sum - ans * 2) && f[i] < f[ans]) ans = i;
    printf("%d", f[ans]);
    return 0;
}
参考程序

猜你喜欢

转载自www.cnblogs.com/kcn999/p/10804729.html
今日推荐