AcWing 328. 芯片 (二进制写法)
我自闭了,调了一下午,我居然认为 \(2, 3\) 凑不出 \(7\),我怕是个孤儿。
这是一位非要用二进制写的勇士。
首先定义状态 \(S\),若 \(S\) 的二进制第 \(k\) 位为 \(1\) 表示那一位下是空闲可以填数的。
再定义一个 \(S(x)\) 表示 \(S\) 下第 \(x\) 位的值。
显然填 \(2 * 3\) 小方格的时候,能不能填取决于最近的三行,和前面无关,我们搞一个二维二进制,通过转移达到目的。
$f[i][j][k] $ 为前 \(i\) 行,第 $ i$ 行状态为 \(j\),第 \(i - 1\) 行状态为 \(k\) 填的最多的小方格数。为什么要用空闲的状态状压而不是填过的呢?因为如果用填过的你不知道在上一行是不是已经填完了。。
转移 \(f[i][j][k] = max( f[i - 1][p][q] + val(p, q, j, k))\)。\(val(p, q, j, k)\) 是两行状态转移的中,以 \(i\) 行为
最下方填写的方格数。
显然复杂度爆炸,我们需要的是,压状态!压、压再压!!由于这是一个最优问题,所以我们可以剪枝,即保留的状态当且仅当里面的 \(1\) (空闲位置)全部放上小方块,以这个原则进行剪枝,可以大大降低复杂度
- 对于一个状态 \(k, j\) 考虑压掉不合法的状态 - \(k(x) = 1, j(x) = 0\) 那个一永远放不上小方块
- \(k(x) = 1, j(x) = 1\) 连续数量必须为偶数,因为到了 \(2\) 个空闲还没扔掉意味着一定是要跨三行,这样列只能 \(2\) 个。
- \(k(x) = 0, j(x) = 1\) 连续数量必须 \(> 1\)
 - 当 \(m = 10\) 最坏情况下,合法的 \(k, j\) 数量只有 \(1123\) 个。 
- 接着考虑合法的 \(q, p\) → \(k, j\) 的转移以及计算 \(val\) - 转移的实质就是在 \(j\) 这一行放可行的方块。 - 如果 \(p(x) = k(x)\) 说明没放新的块,这种情况必须进行续传:
- \(p(x) = k(x) = 1\) 的情况,如果这时候 \(q\) 必须是 \(0\),\(j\) 必须是 \(1\)(不能积累3个空闲块)
 
- 否则说明放了新的块
- \(p(x) = 0, k(x) = 1\),无中生有可还行(减掉
- \(q(x) = 0, p(x) = 1, k(x) = 0\),这时候 \(j(x)\) 必须为 \(0\) 并且连续块是 \(3\) 的倍数(即放横着的块,贡献+)
- \(q(x) = 1, p(x) = 1, k(x) = 0\) 这时候 \(j(x)\) 必须为 \(0\) 且是偶数(放竖着的块,贡献+)
 
 
- 如果 \(p(x) = k(x)\) 说明没放新的块,这种情况必须进行续传:
这样可以的匹配一共有 \(5477\) 个(在 \(m = 10\) 下)
跑的还是很快的 \(740ms\)
#include <cstdio>
#include <iostream>
#include <cstring>
#define x first
#define y second
using namespace std;
const int N = 150, S = 1130, M = 10, L = 5500;
typedef pair<int, int> PII;
int n, m, K, tot, cnt, o, f[N + 1][S], g[N + 1];
PII s[S];
struct Node{
    int a, b, c, y;
} d[L];
bool check(int k, int j) {
    int c1 = 0, c2 = 0;
    for (int i = 0; i < m; i++) {
        if ((k >> i & 1) && !(j >> i & 1)) return false;
        if ((k >> i & 1) && (j >> i & 1)) c2++;
        else {
            if (c2 & 1) return false;
            c2 = 0;
        }
        if (!(k >> i & 1) && (j >> i & 1)) c1++;
        else {
            if (c1 == 1) return false;
            c1 = 0;
        }
    }
    return !(c2 & 1) && (c1 != 1);
}
int get(int q, int p, int k, int j) {
    o = 0;
    int res = 0;
    for (int i = 0; i < m; i++) {
        if ((p >> i & 1) && (k >> i & 1)) {
            if (q >> i & 1 || i + 1 >= m) return -1;
            int u = i + 1;
            if ((q >> u & 1) || !(p >> u & 1) || !(k >> u & 1)) return -1;
            i = i + 1;
        }
        if (!(p >> i & 1) && (k >> i & 1)) return -1;
        if (!(q >> i & 1) && (p >> i & 1) && !(k >> i & 1)) {
            if ((j >> i & 1) || i + 2 >= m) return -1;
            o |= 1 << i;
            res++;
            for (int u = i + 1; u <= i + 2; u++) {
                o |= 1 << u;
                if ((q >> u & 1) || !(p >> u & 1) || (k >> u & 1) || (j >> u & 1)) return -1;
            }
            i = i + 2;
        }
        if ((q >> i & 1) && (p >> i & 1) && !(k >> i & 1)) {
            if ((j >> i & 1) || i + 1 >= m) return -1;
            res++;
            o |= 1 << i;
            int u = i + 1;
            o |= 1 << u;
            if (!(q >> u & 1) || !(p >> u & 1) || (k >> u & 1) || (j >> u & 1)) return -1;
            i = i + 1;
        }
    }
    return res;
}
bool judge(int i, int s) {
    if (i < 1) return true;
    return !(s & g[i]);
}
int main() {
    int T; scanf("%d", &T);
    while (T--) {
        cnt = tot = 0;
        memset(g, 0, sizeof g);
        scanf("%d%d%d", &n, &m, &K);
        for (int i = 1, x, y; i <= K; i++) {
            scanf("%d%d", &x, &y); g[x] |= 1 << (y - 1);
        }
        for (int j = 0; j < (1 << m); j++) {
            for (int k = 0; k < (1 << m); k++) {
                if (check(k, j)) s[tot++] = make_pair(j, k);
            }
        }
        for (int i = 0; i < tot; i++) {
            for (int j = 0; j < tot; j++) {
                int res = get(s[i].y, s[i].x, s[j].y, s[j].x);
                if (res != -1) {
                    d[cnt++] = (Node) { j, i, res, o};
                }
            }
        }
        memset(f, 0xcf, sizeof f);
        f[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < cnt; j++) {
                if (!judge(i - 2, s[d[j].b].y) || !judge(i - 1, s[d[j].b].x) || !judge(i - 1, s[d[j].a].y) || !judge(i, s[d[j].a].x | d[j].y)) continue;
                if (f[i - 1][d[j].b] < 0) continue;
                f[i][d[j].a] = max(f[i][d[j].a], f[i - 1][d[j].b] + d[j].c);
            }
        }
        printf("%d\n", f[n][0]);
    }
}
AcWing 328. 芯片 (二进制写法)的更多相关文章
- java 编码二进制写法、十六进制用源代码表示
		二进制: int a = 0b10; a其实=2 八进制: int a = 01; a其实=8 十六进制: int a = 0x1; a其实=16 
- java:判断二进制数据中第n位是否为1
		可以使用位运算来判断. &是位的与运算符,是指二进制数按位“与”的操作, 逻辑与就是两者都为真的时候才为真,其他真假,假真,假假的运算结果都是假.二进制写法如下 1 & 1 = 1 , ... 
- 51nod 1086 背包问题 V2 【二进制/多重背包】
		1086 背包问题 V2 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 有N种物品,每种物品的数量为C1,C2......Cn.从中任选若干件放 ... 
- [LeetCode] Counting Bits 计数位
		Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the ... 
- [LeetCode] Power of Two 判断2的次方数
		Given an integer, write a function to determine if it is a power of two. Hint: Could you solve it in ... 
- leetcode:Power of Two
		Given an integer, write a function to determine if it is a power of two. 分析:这道题让我们判断一个数是否为2的次方数(而且要求 ... 
- Windows内存管理[转]
		本文主要内容:1.基本概念:物理内存.虚拟内存:物理地址.虚拟地址.逻辑地址:页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一.基本概念1. 两个内存概念物理内存 ... 
- js的基本概念详解
		来自<javascript高级程序设计 第三版:作者Nicholas C. Zakas>的学习笔记(三) 如果你刚学js,想快速了解到js的基本概念,以下将会是一篇不错的引导文章: 语法 ... 
- 浮点数在计算机内存中的表示(IEEE 754规定1位是符号位,8位是指数,剩下的23位为有效数字)
		本文转载自:阮一峰的博客,http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html 张玉彬的博客 h ... 
随机推荐
- ceph在centos7下一个不容易发现的改变
			在centos6以及以前的osd版本,在启动osd的时候,回去根据ceph.conf的配置文件进行挂载osd,然后进行进程的启动,这个格式是这样的 [osd.0] host = hostname de ... 
- [LeetCode题解]109. 有序链表转换二叉搜索树 | 快慢指针 + 递归
			题目描述 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: 给定的有序链表: ... 
- java大厂面经-阿里腾讯、网易美团、京东、华为、快手、字节全在这里了
			前言 在这篇文章详细说了该如何去复习,之前也答应各位把面经整理一下,但是因为入职的事情耽搁了,现在整理出来回馈给大家! 美团 一面 0.自我介绍1.问项目(项目详细介绍.用到什么技术.有什么优化)2. ... 
- 如何在IDM中设置代理服务器?
			很多时候,大家下载文件都是在国外的一些网站上进行下载,这样不可免会受到自身国内网络的限制,另一方面下载源为避免服务器带宽占用过多而限制下载速率,这就会导致文件下载极慢,甚至几KB每秒. 这种情况是不是 ... 
- Guitar Pro怎么导出乐谱
			使用Guitar Pro可以自由创作乐谱,也能根据演示效果来作出相应调整,算得上是公认的良心吉他谱制作软件.除了系统演示功能外,Guitar Pro还能给用户的实际练习提供便利.必要时,用户能将软件内 ... 
- guitar pro系列教程(二十):Guitar Pro使用技巧之使用向导
			本章节将采用图文结合的方式为大家讲述{cms_selflink page='index' text='Guitar Pro'}使用技巧里面的使用向导的相关知识,有兴趣的朋友可以一起来学习哦. 当你创建 ... 
- 如何循序渐进、有效地学习JavaScript?
			转载链接:https://www.zhihu.com/question/19713563/answer/23068003 分享一篇 超毛 的一篇文章<如何学习javascript>(原文链 ... 
- redis 压测与乐观锁
			单线程没有出现并发问题. 链接太多爆炸了 把连接改到50,没有问题 改回1000: emmm159,看来相当一部分拒绝了 并且8180-10000到头了 cpu爆炸了 观察下这种程度的并发用乐观锁 一 ... 
- volatile禁止重排使用场景与单例模式的Double Check Lock
			普通单例模式Demo public class Demo{ private static Demo INSTANCE; private Demo(){} public static Demo getI ... 
- oracle 导入导出表,库
			Exp/Imp是oracle备份数据的两个命令行工具 1.本地数据库导入导出 1.导出 (运行---cmd中操作) exp 用户名/密码@数据库实例名file=本地存放路径 eg: exp jnjp/ ... 
