多米诺骨牌由上下 \(2\) 个方块组成,每个方块中有 \(1 \sim 6\) 个点。现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S_2\),它们的差为 \(\left| S_1 - S_2 \right|\)。如图,

\[S_1 = 6 + 1 + 1 + 1 = 9,
S_2 = 1 + 5 + 3 + 2 = 11,
\left| S_1 - S_2 \right| = 2
\]

每个多米诺骨牌可以旋转 \(180°\),使得上下两个方块互换位置。请你计算最少旋转多少次才能使多米诺骨牌上下 \(2\) 行点数之差达到最小。对于图中的例子,只要将最后一个多米诺骨牌旋转 \(180°\),即可使上下 \(2\) 行点数之差为 \(0\)。

输入格式

输入文件的第一行是一个正整数 \(n (1\leq n\leq 1000)\),表示多米诺骨牌数。接下来的 \(n\) 行表示 \(n\) 个多米诺骨牌的点数。每行有两个用空格隔开的正整数,表示多米诺骨牌上下方块中的点数 \(a\) 和 \(b\),且 \(1\leq a,b\leq 6\)。

输出格式

输出文件仅一行,包含一个整数。表示求得的最小旋转次数。

输入输出样例

输入:

4
6 1
1 5
1 3
1 2

输出:

1

题解

可以考虑用动态规划。该问题要求解的状态为,多米诺骨牌上下两行点数之差最小同时旋转次数最少

使用二维 \(dp\) 数组记录上下点数之差旋转次数。由于对一堆骨牌的旋转操作不会改变这堆骨牌的点数之和,如果记录了上点数,下点数就为总点数 \(-\) 上点数(总点数在输入时预处理即可得到)。此时我们可以具体地说,\(dp[i][j]\) 表示前 \(i\) 个骨牌,上一行点数之和为 \(j\) 时的最小旋转次数。

状态转移方程

\(dp[i][j]\) 表示前 \(i\) 个骨牌,上一行点数之和为 \(j\) 时的最小旋转次数。

对于第 \(i\) 个骨牌,它可以选择“旋转”或是“不旋转”。如果“不旋转”,则此时的最小旋转次数需由 \(dp[i - 1][j - a[i]]\)确定,即前 \(i - 1\) 个骨牌,上一行点数之和为 \(j - a[i]\) 时的最小旋转次数决定;如果“旋转”,则此时的最小旋转次数需由 \(dp[i - 1][j - b[i]]\)确定,即前 \(i - 1\) 个骨牌,上一行点数之和为 \(j - b[i]\) 时的最小旋转次数决定。

可以写出如下状态转移方程:

\[dp[i][j] =
\min (dp[i - 1][j - a[i]], dp[i - 1][j - b[i]] + 1)
\]

状态搜索方向

初始化二维 \(dp\) 数组的第一行,然后递增行数,对每一行从左至右搜索。

初始值所有的 \(dp[i][j]\) 为 \(\inf\),\(dp[1][b[1]] = 1\),\(dp[1][a[1]] = 0\)(注意初始化顺序不能交换,想想为什么??)。

考虑我们的状态搜索方向,在更新 \(dp[i][j]\) 的值时,\(i - 1\)是完全正确的,但 \(j\)、\(a[i]\)、\(b[i]\)会出现三种情况。

  1. \(j\) 大于等于这两者;
  2. \(j\) 只大于等于其中一个,而小于另一个;
  3. \(j\) 小于这两者;

此时如果 \(j < a[i]\),则 \(dp[i][j]\) 不能由状态 \(dp[i - 1][j - a[i]]\)(该状态此时不存在)确定,如果 \(j < b[i]\),则 \(dp[i][j]\) 不能由状态 \(dp[i - 1][j - b[i]]\)(该状态不存在)确定,不难想象,若 \(j < a[i]\) 且 \(j < b[i]\),则 \(dp[i][j]\) 为 \(\inf\)(用有限状态机的观点来考虑,可以认为该状态不可达)。

否则,\(dp[i][j]\) 由且只由 \(dp[i - 1][j - a[i]]\) 和 \(dp[i - 1][j - b[i]]\) 这两个状态中那些存在中的状态确定。如果存在中的所有状态(最多两个)最小旋转次数的值都为 \(\inf\),则转移方程确定 \(dp[i][j]\) 为 \(\inf\)(即该状态不可达),而事实上,由二维 \(dp\) 数组的含义来看 \(dp[i][j]\) 也确实应该不可达;如果其中一个存在状态的值不为 \(\inf\),则 \(dp[i][j]\) 一定被更新为一个有限数(更新规则由转移方程确定)。

为了编写程序,可将状态转移方程写成以下等价形式:

\[dp[i][j] =
\left\{
\begin{aligned}
& \min (dp[i][j], dp[i - 1][j - a[i]]), & if (j >= a[i]) \\
& \min (dp[i][j], dp[i - 1][j - b[i]] + 1), & if (j >= b[i])
\end{aligned}
\right.
\]

易证两个状态转移方程的等价性(程序第一次更新 \(dp[i][j]\) 时,该值一定为 \(\inf\))。

程序:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; const int N = 1000;
const int INF = 1e8;
int a[N + 10], b[N + 10], dp[N + 10][6 * N + 10]; int main()
{
int n;
scanf("%d", &n);
int s = 0;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i], &b[i]);
s += a[i] + b[i];
}
// 状态矩阵初始化
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 6 * n; j++) dp[i][j] = INF;
dp[1][b[1]] = 1; dp[1][a[1]] = 0;
for (int i = 2; i <= n; i++) {
for (int j = 0; j <= 6 * n; j++) {
// 状态转移方程
if (j - a[i] >= 0) dp[i][j] = min(dp[i][j], dp[i - 1][j - a[i]]);
if (j - b[i] >= 0) dp[i][j] = min(dp[i][j], dp[i - 1][j - b[i]] + 1);
}
}
int minD = INF, minT = INF; //minD是最小差值,minT是最小交换次数
for (int i = 0; i <= s; i++) {
if (dp[n][i] != INF) {
if (abs(i - (s - i)) < minD) {
minD = abs(i - (s - i)); minT = dp[n][i];
}
else if (abs(i - (s - i)) == minD) minT = min(minT, dp[n][i]);
}
}
printf("%d", minT);
return 0;
}

ACM - 动态规划 - P1282 多米诺骨牌的更多相关文章

  1. poj 1717==洛谷P1282 多米诺骨牌

    Dominoes Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6571   Accepted: 2178 Descript ...

  2. P1282 多米诺骨牌

    P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S ...

  3. 洛谷P1282 多米诺骨牌 (DP)

    洛谷P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中 ...

  4. 洛谷P1282 多米诺骨牌

    P1282 多米诺骨牌 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S ...

  5. P1282 多米诺骨牌【dp】

    P1282 多米诺骨牌 提交 20.02k 通过 6.30k 时间限制 1.00s 内存限制 125.00MB 题目提供者洛谷 难度提高+/省选- 历史分数100 提交记录 查看题解 标签   查看算 ...

  6. 【01背包】洛谷P1282多米诺骨牌

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  7. P1282 多米诺骨牌 (背包变形问题)

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  8. P1282 多米诺骨牌 (差值DP+背包)

    题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+1+1+1=9, ...

  9. P1282 多米诺骨牌[可行性01背包]

    题目来源:洛谷 题目描述 多米诺骨牌有上下2个方块组成,每个方块中有1~6个点.现有排成行的 上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|.例如在图8-1中,S1=6+ ...

随机推荐

  1. 【C# 线程】 atomic action原子操作|primitive(基元、原语)

    概念 原子操作(atomic action):也叫primitive(原语.基元),它是操作系统用语范畴.指由若干条指令组成的,用于完成一定功能的一个过程.  原语是由若干个机器指令构成的完成某种特定 ...

  2. 谷歌集群数据 clusterdata-2011-2 Cluster workload traces

    谷歌集群数据 clusterdata-2011-2 https://github.com/google/cluster-data/blob/master/ClusterData2011_2.md 链接 ...

  3. C# 使用NPOI处理Excel模板-【前面部分固定,中间是动态的几行,尾部是固定的部分】

    今天同组的兄弟问我,他有一个导出的模板,大概如下: [前面部分固定,中间是动态的几行,尾部是固定的部分].其实这个很像单链表往单链表在指定插入数据. 他问我怎么做才好,他想到的做法是:因为这些动态列的 ...

  4. AcWing 215. 破译密码

    传送门 思路:gcd(a,b)=k<=>gcd(a/k,b/k)=1,令x=a/k,y=b/k,则问题变为问x<=a/d,y<=b/d有多少(x,y)满足gcd(x,y)=1. ...

  5. 【译】C# 11 特性的早期预览

    原文 | Kathleen 翻译 | 郑子铭 Visual Studio 17.1(Visual Studio 2022 Update 1)和 .NET SDK 6.0.200 包含 C# 11 的预 ...

  6. Qt:Qt资源系统

    学习自 Qt 资源系统(Qt Resource System) - 知乎 1.什么是Qt 资源系统 Qt资源系统是一种将图片.数据存储于二进制文件中的一套系统.这些图片.数据会被我们的程序使用,它们称 ...

  7. Qt:QTableWidget

    0.说明 QTableWidget类提供了一个基于Item的Table视图,如下图: Table Widget提供了表格用于显示.Table中的每个Item都是QTableWidgetItem对象. ...

  8. .Net/C#分库分表高性能O(1)瀑布流分页

    .Net/C#分库分表高性能O(1)瀑布流分页 框架介绍 依照惯例首先介绍本期主角:ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本. ...

  9. 二级py--day5 软件工程基础

    二级py--day5软件工程基础 软件工程基础 1.软件工程三要素:方法.工具和过程 2.软件生命周期可以分为:项目可行性研究与规划.软件需求分析.软件设计.软件实现.软件测试.软件运行与维护等阶段 ...

  10. source tree的使用

    参看博客: https://blog.csdn.net/syq8023/article/details/89844030 http://jingyan.baidu.com/album/59703552 ...