LuoguP1282 - 多米诺骨牌

【LuoguP1041】多米诺骨牌
题目链接: https://www.luogu.org/problem/P1282

前言

最近重点做动态规划。今天自己成功推出了两三道题,然后开始水多年未更新的博客?

不能颓废!

分析

首先看数据范围$(N <= 1000, A_i/B_i <= 6)$,那么搜索当然不可能,肯定是考虑动态规划。

还是回归搜索上,比较好分析。一个牌只有两种状态,旋转与不旋转。我们只需要先找到最小差,再考虑最小旋转次数。

再看数据范围,$O(6N^2)$不会超时。我们既要求最小差又要求最小最小旋转次数,又可知所有数的总和是确定的,不如就设个背包。

$F[N(1000)][SUM(6000)]$表示处理到第N个点,上方块的和为SUM时最小的旋转次数。

一开始除$F[0]$为0外都赋值为无穷大,然后转移$A_i$表示上方块,$B_i$表示下方块。

$$F[N][i] = min(F[N - 1][i - A_i], F[N - 1][i - B_i] + 1)$$

然后由于比较闲(怕超空间,然而并不会?)。显然,只要记录N-1的状态,就开了滚动数组$F[2][SUM]$。

最后先找最小差,我们计算$X = \lfloor\frac{SUM}{2} \rfloor$。

如果$SUM$为奇,那么上下差不可能为0,并且上方块和为$X$或$X + 1$时差都为1,因此再设置$Y = X + 1$。

如果$SUM$为偶,那么上方块和为$X$时上下差就为0,$Y = X$。

我们枚举一个数$i$从0开始,不断增大,表示一个范围(大概是这样)。

判断$F[N][X - i]$和$F[N][Y + i]$是否有值,若有则输出小的那个。

代码

代码较丑,推荐自己写代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int n, a[1005], b[1005], c[1005], f[2][8000], sum;
int main() {
cin >> n;
for(int i= 1; i <= n; i++) cin >> a[i] >> b[i], c[i]= a[i] - b[i], sum+= a[i] + b[i];
memset(f, 0x3f, sizeof(f));
f[0][0]= 0;
for(int i= 1; i <= n; i++) {
for(int j= 0; j <= sum; j++) f[i & 1][j]= 0x3f3f3f3f;
for(int j= sum; j >= a[i]; j--) f[i & 1][j]= f[(i & 1) ^ 1][j - a[i]];
for(int j= sum; j >= b[i]; j--) f[i & 1][j]= min(f[i & 1][j], f[(i & 1) ^ 1][j - b[i]] + 1);
}
int res= 0x3f3f3f3f, sum1= sum / 2, sum2= sum1 + sum % 2;
for(int i= 0; i < sum; i++) {
if(sum1 + i > n && sum2 - i < 1) break;
res= min(f[n & 1][sum1 + i], f[n & 1][sum2 - i]);
if(f[n & 1][sum1 + i] != 0x3f3f3f3f || f[n & 1][sum2 - i] != 0x3f3f3f3f) break;
}
cout << res << endl;
return 0;
}

后记

我的代码必须开8000,如果开8000以下好像会RE。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×