多米诺骨牌题解

Posted kcfzyhq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多米诺骨牌题解相关的知识,希望对你有一定的参考价值。

题目描述

多米诺骨牌有上下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

【题解】
将复杂的问题简化了,根据题目要求,就是要求最小的翻转步数使得所有骰子的上下差之和最小
这样的描述很像是背包问题的描述:在给定容量的背包内放最大价值的物品。
唯一的差别就在于最小与给定,但这不影响做题
有了表层的理解我们不难仿造出类似背包且是01背包问题的状态转移方程
\(f(i,j)=\max\{f(i-1,j),f(i-1,j-2*(b[i]-a[i])\}\)
条件为\((j-2*(b[i]-a[i])\ge 0)\)初始条件为\(f[1][tot]=0\),tot为初始状态上下所有差之和
代表的意义是
前i个使得上下差之和为j的最小步数**
关于\((j-2*(b[i]-a[i])\ge 0)\)可以很简单地推导出来:
初始是\(a-b\)
那么翻转之后为\(b-a\)
那么两者相差\((b-a)-(a-b)=2*(b-a)\)
证毕
所以代码以简单地写出来:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int n,a[1010],b[1010],f[2][15000],tot=0,sum=0;

int main() {
    memset(f,inf,sizeof(f));
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&a[i],&b[i]),tot+=a[i]-b[i],sum+=abs(a[i]-b[i]);
    f[1][sum+tot]=0;
    tot=sum;
    tot<<=1;
    int t=0;
    for(int i=1; i<=n; i++,t^=1) {
        for(int j=0; j<=tot; j++) {
            f[t][j]=min(f[t][j],f[t^1][j]);
            if(j-2*(b[i]-a[i])>=0&&j-2*(b[i]-a[i])<=tot)
                f[t][j]=min(f[t][j],f[t^1][j-2*(b[i]-a[i])]+1);
        }
    }
    tot>>=1;
    for(int i=0; i<=tot; i++) {
        int k=min(f[t^1][tot-i],f[t^1][tot+i]);
        if(k!=inf) {
            printf("%d\n",k);
            break;
        }
    }
    return 0;
}

这是调试的代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int n,a[1010],b[1010],f[2][15000],tot=0,sum=0;


int main() {
    memset(f,inf,sizeof(f));
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
        scanf("%d%d",&a[i],&b[i]),tot+=a[i]-b[i],sum+=abs(a[i]-b[i]);
    //printf("%d %d\n", sum, tot);
//  printf("%d %d\n",sum,tot);
    /*f[1][sum-(b[1]-a[1])*2]=1,*/f[1][sum+tot]=0;
    tot=sum;
    tot<<=1;
    int t=0;
//  puts("-------");
    for(int i=1; i<=n; i++,t^=1) {
        for(int j=0; j<=tot; j++) {
            f[t][j]=min(f[t][j],f[t^1][j]);
            if(j-2*(b[i]-a[i])>=0&&j-2*(b[i]-a[i])<=tot)
                f[t][j]=min(f[t][j],f[t^1][j-2*(b[i]-a[i])]+1);
        //  if(j+2*(b[i]-a[i])>=0&&j+2*(b[i]-a[i])<=tot)f[t][j+2*(b[i]-a[i])]=min(f[t][j+2*(b[i]-a[i])],f[t^1][j]+1); //翻
        //  if(j+2*(b[i]-a[i])>=0&&j+2*(b[i]-a[i])<=tot)printf("(%d,%d) ",j,j+2*(b[i]-a[i]));
        //  if(f[t][j]==inf)printf("inf ");
    //      else
        //  printf("%3d ",f[t][j]);
        }
    //  puts("");
    }
//  puts("-------");
    tot>>=1;
    for(int i=0; i<=tot; i++) {
        int k=min(f[t^1][tot-i],f[t^1][tot+i]);
        if(k!=inf) {
            printf("%d\n",k);
            break;
        }
    }
    return 0;
}






















以上是关于多米诺骨牌题解的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #742 (Div. 2) 题解

题解骨牌

多米诺骨牌

Codeforces Round #742 div.2 A-F题解

Codeforces Round #742 div.2 A-F题解

学长出题比赛题解17-09-29