题目链接 Clear The Matrix

题意 给定一个$4 * n$的矩形,里面的元素为$'.'$或$'*'$。现在有$4$种正方形可以覆盖掉$'*'$,正方形的边长分别为$1,2,3,4$。

求把整个矩形变成全$'.'$的最小代价。

考虑状压DP

设$f[i][j]$为前$i$列已经全部变成'.',第$i + 1$到第$i + 4$列的这$16$个格子状态为$j$的最小花费。

这$16$个格子标号如下

$0$   $4$   $8$   $12$

$1$   $5$   $9$   $13$

$2$   $6$  $10$  $14$

$3$   $7$  $11$  $15$

我们可以枚举$0,1,2,3$这$4$个格子。以当前格子为左上角的正方形的边长。

其中$0$号格子可以放边长为$0, 1, 2, 3, 4$的正方形;

$1$号格子可以放边长为$0, 1, 2, 3$的正方形;

$2$号格子可以放边长为$0, 1, 2$的正方形;

$3$号格子可以放边长为$0, 1$的正方形;

放边长为$0$的正方形等效为不放。

当枚举的这些正方形可以完全盖住$0,1,2,3$这$4$个格子的时候,就可以进行状态转移。

状态稍微有点复杂,用二进制位表示……

时间复杂度$O(n * 2^{16} * 5!)$

#include <bits/stdc++.h>using namespace std;#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b)    for (int i(a); i >= (b); --i)const int N = 1e3 + 10;
const int S = 1 << 16;char s[N];
int  f[N][S + 2];
int  a[6][N];
int  c[10];
int  g[10];
int  n;
int  pre[N];
int  ans;
int  cnt, mask;void up(int &a, int b){ if (a > b) a = b;}
inline get(int x){ return x ^ (S - 1);}int main(){scanf("%d", &n);rep(i, 1, 4) scanf("%d", c + i);rep(i, 1, 4){scanf("%s", s + 1);rep(j, 1, n) a[i][j] = s[j] == '*';}rep(k, 0, n){rep(i, 0, S + 1) f[k][i] = 1e9;}cnt = -1;mask = 0;rep(i, 1, 4){rep(j, 1, 4){++cnt;if (a[j][i]) mask |= (1 << cnt);}}f[0][mask] = 0;g[0] = 0;g[1] = 1;g[2] = (1 << 0) ^ (1 << 1) ^ (1 << 4) ^ (1 << 5);g[3] = (1 << 0) ^ (1 << 1) ^ (1 << 2);g[3] ^= ((1 << 4) ^ (1 << 5) ^ (1 << 6));g[3] ^= ((1 << 8) ^ (1 << 9) ^ (1 << 10));g[4] = (1 << 16) - 1;rep(k, 0, n){int extra = 0;rep(j, 1, 4) if (a[j][k + 5]) extra |= (1 << (j + 11));rep(j, 0, S - 1){if (f[k][j] >= 1e9) continue;rep(aa, 0, 4){rep(bb, 0, 3){rep(cc, 0, 2){rep(dd, 0, 1){int cnt = get(g[aa]) & get(g[bb] << 1) & get(g[cc] << 2) & get(g[dd] << 3);if ((cnt & j & 15) == 0){int nowmask = cnt & j;nowmask >>= 4;nowmask ^=  extra;up(f[k + 1][nowmask], f[k][j] + c[aa] + c[bb] + c[cc] + c[dd]); }}}}}}}ans = 1e9;rep(i, n - 4, n) ans = min(ans, f[i][0]);printf("%d\n", ans);return 0;
}

 

我们可以考虑使用滚动数组,于是空间大大节省

#include <bits/stdc++.h>using namespace std;#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b)    for (int i(a); i >= (b); --i)
#define MP      make_pair
#define fi      first
#define se      secondtypedef long long LL;const int N = 1e3 + 10;
const int S = 1 << 16;char s[N];
int  f[2][S + 2];
int  a[6][N];
int  c[10];
int  g[10];
int  n;
int  pre;
int  ans;
int  cnt, mask;void up(int &a, int b){ if (a > b) a = b;}
inline get(int x){ return x ^ (S - 1);}int main(){scanf("%d", &n);rep(i, 1, 4) scanf("%d", c + i);rep(i, 1, 4){scanf("%s", s + 1);rep(j, 1, n) a[i][j] = s[j] == '*';}rep(k, 0, 1) rep(i, 0, S + 1) f[k][i] = 1e9;cnt = -1;mask = 0;rep(i, 1, 4){rep(j, 1, 4){++cnt;if (a[j][i]) mask |= (1 << cnt);}}f[0][mask] = 0;pre = 0;g[0] = 0;g[1] = 1;g[2] = (1 << 0) ^ (1 << 1) ^ (1 << 4) ^ (1 << 5);g[3] = (1 << 0) ^ (1 << 1) ^ (1 << 2);g[3] ^= ((1 << 4) ^ (1 << 5) ^ (1 << 6));g[3] ^= ((1 << 8) ^ (1 << 9) ^ (1 << 10));g[4] = (1 << 16) - 1;rep(i, 0, n){int extra = 0;rep(j, 1, 4) if (a[j][i + 5]) extra |= (1 << (j + 11));rep(j, 0, S + 1) f[pre ^ 1][j] = 1e9;rep(j, 0, S - 1){if (f[pre][j] >= 1e9) continue;rep(aa, 0, 4){rep(bb, 0, 3){rep(cc, 0, 2){rep(dd, 0, 1){int cnt = get(g[aa]) & get(g[bb] << 1) & get(g[cc] << 2) & get(g[dd] << 3);if ((cnt & j & 15) == 0){int nowmask = cnt & j;nowmask >>= 4;nowmask ^=  extra;up(f[pre ^ 1][nowmask], f[pre][j] + c[aa] + c[bb] + c[cc] + c[dd]); }}}}}}pre ^= 1;}printf("%d\n", f[pre][0]);return 0;
}

不过这个做法还不是最优的= =

官方题解给出的做法是只存后面12个格子的状态的

因为当考虑某一列的时候一旦用到$4*4$的正方形,其他边长的正方形就不用再考虑了……直接无视掉。

这样的话可以直接从$f[k][nowmask]$转移到$f[k + 1][0]$

时间复杂度$O(n * 2^{12} * 96)$

#include <bits/stdc++.h>using namespace std;#define rep(i, a, b)   for (int i(a); i <= (b); ++i)
#define dec(i, a, b)    for (int i(a); i >= (b); --i)const int N = 1e3 + 10;
const int S = 1 << 12;char s[N];
int  f[2][S + 2], a[6][N], c[10], g[10];
int  n, x, cnt, mask, ans;void up(int &a, int b){ if (a > b) a = b;}
inline get(int x){ return x ^ (S - 1);}int main(){scanf("%d", &n);rep(i, 1, 4) scanf("%d", c + i);rep(i, 1, 4){scanf("%s", s + 1);rep(j, 1, n) a[i][j] = s[j] == '*';}rep(k, 0, 1) rep(i, 0, S + 1) f[k][i] = 1e9;cnt = -1;mask = 0;rep(i, 1, 3){ rep(j, 1, 4){  ++cnt; if (a[j][i]) mask |= (1 << cnt); }}f[0][mask] = 0;x = 0;g[0] = 0;g[1] = 1;g[2] = 51;g[3] = 1911;rep(i, 0, n){int extra = 0;rep(j, 1, 4) if (a[j][i + 4]) extra |= (1 << (j + 7));rep(j, 0, S + 1) f[x ^ 1][j] = 1e9;rep(j, 0, S - 1){if (f[x][j] >= 1e9) continue;rep(aa, 0, 3){rep(bb, 0, 3){rep(cc, 0, 2){rep(dd, 0, 1){int cnt = get(g[aa]) & get(g[bb] << 1) & get(g[cc] << 2) & get(g[dd] << 3);if ((cnt & j & 15) == 0){mask = (cnt & j) >> 4;mask ^=  extra;up(f[x ^ 1][mask], f[x][j] + c[aa] + c[bb] + c[cc] + c[dd]); }}}}}up(f[x ^ 1][0], f[x][j] + c[4]);}x ^= 1;}printf("%d\n", f[x][0]);return 0;
}

转载于:https://www.cnblogs.com/cxhscst2/p/8253566.html

Codeforces 903F Clear The Matrix(状态压缩DP)相关推荐

  1. Light OJ 1316 A Wedding Party 最短路+状态压缩DP

    题目来源:Light OJ 1316 1316 - A Wedding Party 题意:和HDU 4284 差点儿相同 有一些商店 从起点到终点在走过尽量多商店的情况下求最短路 思路:首先预处理每两 ...

  2. 第一章 动态规划 状态压缩DP

    1.基本概述 状态压缩dp和状态机一样,都是一种特殊的状态表示方式.状态机用一系列小状态表示某一状态.状态压缩dp用二进制数进行表示.虽然看代码起来时间复杂度比较高,但是很多的情况都给剪枝掉了. 状态 ...

  3. 【AcWing】数位统计DP、树形DP、状态压缩DP、记忆化搜索

    [AcWing]数位统计DP.树形DP.状态压缩DP.记忆化搜索 一.数位统计DP 二.状态压缩DP 三.树形DP 四.记忆化搜索 一.数位统计DP 计数问题 给定两个整数 a 和 b,求 a 和 b ...

  4. 0x56. 动态规划 - 状态压缩DP(习题详解 × 7)

    目录 Problem A. 最短Hamilton路径 ProblemB. 蒙德里安的梦想 Problem C. Corn Fields Problem D. 小国王 Problem E. 炮兵阵地 P ...

  5. POJ 2411 Mondriaan‘s Dream(最清楚好懂的状压DP讲解)(连通性状态压缩DP)

    poj 2411 Mondriaan's Dream(最清晰的状压DP解析) 闫氏DP大法好 我们这里是一列一列地来,因为是一个棋盘性的状态压缩DP,从哪个方向都一样 摆放的小方格总方案数 等价于 横 ...

  6. BZOJ1688|二进制枚举子集| 状态压缩DP

    Disease Manangement 疾病管理 Description Alas! A set of D (1 <= D <= 15) diseases (numbered 1..D) ...

  7. hdu1074 状态压缩dp+记录方案

    题意:       给你一些作业,每个作业有自己的结束时间和花费时间,如果超过结束时间完成,一天扣一分,问你把n个作业完成最少的扣分,要求输出方案. 思路:       状态压缩dp,记录方案数的地方 ...

  8. FZU-2218 Simple String Problem(状态压缩DP)

    原题地址: 题意: 给你一个串和两个整数n和k,n表示串的长度,k表示串只有前k个小写字母,问你两个不含相同元素的连续子串的长度的最大乘积. 思路: 状态压缩DP最多16位,第i位的状态表示第i位字母 ...

  9. 《算法竞赛进阶指南》打卡-基本算法-AcWing 91. 最短Hamilton路径:位运算、状态压缩dp、dp

    文章目录 题目解答 题目链接 题目解答 分析: 状态压缩dp是用二进制数来表示状态. 数据范围n = 20, 那么状态总量就是2202^{20}220个状态. 可以按照以下思路去思考: 哪些点被用过 ...

最新文章

  1. mondb 常用命令学习记录
  2. Nmap扫描教程之Nmap基础知识
  3. Spring Cloud(七) GateWay 服务化和过滤器
  4. 速卖通消费电子行业市场分析热销及需求品类推荐
  5. I Hate It(线段树)
  6. “入洞房与度蜜月”的来历
  7. Volatile原子性一致性JVM指令重排
  8. 米熊科技:给烘培加点“云”的味道
  9. PHP版本选择讲解:VC6与VC9,Thread Safe与None-Thread Safe等的选择
  10. php核心语法,PHP核心语法总结
  11. GitHub上最流行的10000个Java都使用了哪些库?
  12. Chrome内核解析 -- 背景篇:Chrome, Chromium, WebKit, WebKit2, Blink
  13. java高并发解决方案
  14. VMware Authorization Service 未运行的解决办法
  15. python获取电脑屏幕分辨率
  16. 世界上最著名也最危险的APT恶意软件清单
  17. 根据经纬度计算距离(百度地图)
  18. pc端生成支付宝支付二维码
  19. 【Unity/C#】游戏出现区域性崩溃,深藏的国际化巨坑
  20. 一个优秀IT专家的成长历程---献给所有的颓废或..

热门文章

  1. Python+OpenCV 图像处理系列(1)— Ubuntu OpenCV安装、图片加载、显示和保存
  2. Linux df -h 显示磁盘空间满,但实际未占用满——问题分析
  3. 只要5分钟用数据可视化带你看遍11月份新闻热点事件
  4. 朴素贝叶斯与逻辑回归区别
  5. Java集合详解之Map
  6. SpringBoot配置文件YAML配置注入(详解)
  7. SoC(System on chip)与NoC(network-on-chip)
  8. GPU上的快速光谱图分区
  9. 标题 相机标定(Camera calibration)原理和步骤
  10. 【C语言】一些简单编译错误或警告