算法竞赛备赛进阶之状态压缩训练

news2024/11/28 10:42:18

状态压缩

状态压缩DP是一种暴力的算法,它需要遍历每个状态,而每个状态是多个事件的集合。这种算法通常用于小规模问题的求解,因为它的复杂度是指数级别的。

状态压缩DP的两个基本特征包括问题的数据规模特别小,可以通过2的阶乘次进行求解,且题目通常都是选与不选两种选择,可以使用二进制串表示。

状态压缩DP通常使用二进制数来表示状态。一个数就能表示一个状态,通常一个状态数据就是一个一串0和1组成的二进制数,每一位二进制数只有两种状态,比如说硬币的正反两面,10枚硬币的结果就可以用10位二进制数完全表示出来,每一个10位二进制数就表示了其中一种结果。使用二进制数表示状态不仅缩小了数据存储空间,还能利用二进制数的位运算很方便地进行状态转移。

状态压缩DP:

  1. 状态表示f[i, j, s]

    1. 集合:所有只摆在前i行,已经摆了j个数据,并且在第i行摆放的状态是s的所有方案的集合

    2. 属性:Count

  2. 状态计算

已经摆完前i排,并且第i排的状态a,第i-1排的状态是b,已经摆了j个物品的所有方案。

已经摆完前i-1排,并且第i-1排的状态是b,已经摆了j-count(a)个物品的所有方案,f[i-1, j-count(a), b]

  1. 第i-1行内部不能有两个1相邻

  2. 第i-1行和第i行之间也不能相互攻击到

  3. (a & b) == 0 (a | b)不能有两个相邻的1

  4. 状态数量*状态转移的计算量

1.小国王

在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。 输入格式 共一行,包含两个整数 n 和 k。 输出格式 共一行,表示方案总数,若不能够放置则输出0。 数据范围 1≤n≤10, 0≤k≤n2

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
​
using namespace std;
​
const int N = 12, M = 1 << 10, k = 110;
​
typedef long long LL;
​
int n, m;
vector<int> state;
int cnt[M];
vector<int> head[M];
LL f[N][K][M];
​
bool check(int state)
{
    for(int i = 0;i < n; i++)
        if((state >> i & 1) && (state >> i + & 1))
            return false;
    return true;
}
​
int count(int state)
{
    int res = 0;
    for(int i = 0;i < n; i++) res += state >> i & 1;
    return res;
}
​
int main()
{
    cin >> n >> m;
    
    for(int i = 0;i < n; i++)
        if(check(i))
        {
            state.push_back(i);
            cnt[i] = count(i);
        }
    
    for(int i = 0;i < state.size(); i++)
        for(int j = 0;j < state.size(); j++)
        {
            int a = state[i], b = state[j];
            if((a % b) == 0 && check(a | b))
                head[a].push_back(b);
        }
    
    f[0][0][0] = 1;
    for(int i = 1;i <= n + 1; i++)
        for(int j = 0;j <= m; j++)
            for(int a = 0;a < state.size(); a++)
                for(int b : head[a])
                {
                    int c = cnr[state[a]];
                    if(j >= c)
                        f[i][j][a] += f[i - 1][j - c][b];
                }
    
    cout << f[n + 1][m][0] << endl;
    
    return 0;
}

2.玉米田

农夫约翰的土地由 M×N 个小方格组成,现在他要在土地里种植玉米。

非常遗憾,部分土地是不育的,无法种植。

而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。

现在给定土地的大小,请你求出共有多少种种植方法。

土地上什么都不种也算一种方法。

输入格式

第 1 行包含两个整数 M 和 N。

第 2..M+1 行:每行包含 N 个整数 0 或 1,用来描述整个土地的状况,1 表示该块土地肥沃,0 表示该块土地不育。

#include<iostream>
#include<algorithm>
#include<vector>
​
using namespace std;
​
const int N = 14, M = 1 << 12, mod = 1e8;
​
int n, m;
int g[N];
vector <int> state;
vector <int> head[M];
int f[N][M];
​
bool check(int state)
{
    for(int i = 0;i < m; i++)
        if((state >> i & 1) && (state >> i + 1 & 1))
            return false;
    return true;
}
​
int main()
{
    scanf("%d%d", &n, &m);
    
    for(int i = 1;i <= n; i++)
        for(int j = 0;j < m; j++)
        {
            int t;
            cin >> t;
            g[i] += |t << j;
        }
    
    for(int i = 0;i < 1 << m; i++)
        if(check(i))
            state.push_back(i);
    
    for(int i = 0;i < state.size(); i++)
        for(int j = 0;j < state.size(); j++)
        {
            int a = state[i], b = state[j];
            if((a & b) == 0)
                head[i].push_back(j);
        }
    
    f[0][0] = 1;
    for(int i = 1;i <= n + 1; i++)
        for(int a = 0;a < state.size(); a++)
            for(int b : head[a])
            {
                if(g[i] & state[a]) continue;
                f[i][a] = (f[i][a] + f[i - 1][b]) % mod;
            }
    
    printf("%d\n", f[n + 1][0]);
    return 0;
}

3.炮兵阵地

司令部的将军们打算在 N×M 的网格地图上部署他们的炮兵部队。

一个 N×M 的地图由 N 行 M 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

1185_1.jpg

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。

从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示 N 和 M;

接下来的 N 行,每一行含有连续的 M 个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。

已经摆完前i行,且第i行的状态是a,第i-1行的状态是b的所有摆放方案。

已经摆完前i-1行,且第i-1行的状态是b的所有摆放方案。f[i-1, b]

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
​
using namespace std;
​
const int N = 11, M = 1 << 10;
​
int n, m;
int g[110];
vector<int> state;
int f[2][M][M];
int cnt[M];
​
bool check(int state)
{
    for(int i = 0;i < m; i++)
        if((state >> i & 1) && ((state >> i + 1 & 1) | (state >> i + 2 & 1)))
            return false;
    return true;
}
​
int count(int state)
{
    int res = 0;
    for(int i = 0;i < m; i++) res += state >> i & 1;
    return res;
}
​
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n; i++)
        for(int j = 0;j < m; j++)
        {
            char c;
            cin >> c;
            if(c == 'H') g[i] += 1 << j;
        }
    
    for(int i = 0;i < 1 << m; i++)
        if(check(i))
        {
            state.push_back(i);
            cnt[i] = count(i);
        }
    
    for(int i = 1; i<= n + 2; i++)
        for(int j = 0;j < state.size(); j++)
            for(int k = 0;k < state.size(); k++)
                for(int u = 0;u < state.size(); u++)
                {
                    int a = state[j], b = state[k], c = state[u];
                    if((a & b) | (b & c) | (a & c)) continue;
                    if(g[i - 1] & a | g[i] & b) continue;
                    f[i & 1][j][k] = max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[b]);
                }
    
    cout << f[n + 2 & 1][0][0] << endl;
    
    return 0;
}

4.愤怒的小鸟

Kiana 最近沉迷于一款神奇的游戏无法自拔。   

简单来说,这款游戏是在一个平面上进行的。 

有一架弹弓位于 (0,0)(0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟, 小鸟们的飞行轨迹均为形如 y=ax2+bxy=ax2+bx 的曲线,其中 a,ba,b 是 Kiana 指定的参数,且必须满足 a<0a<0。

当小鸟落回地面(即 xx 轴)时,它就会瞬间消失。

在游戏的某个关卡里,平面的第一象限中有 nn 只绿色的小猪,其中第 ii 只小猪所在的坐标为 (xi,yi)(xi,yi)。 

如果某只小鸟的飞行轨迹经过了 (xi, yi)(xi, yi),那么第 ii 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行; 

如果一只小鸟的飞行轨迹没有经过 (xi, yi)(xi, yi),那么这只小鸟飞行的全过程就不会对第 ii 只小猪产生任何影响。 

例如,若两只小猪分别位于 (1,3)(1,3) 和 (3,3)(3,3),Kiana 可以选择发射一只飞行轨迹为 y=−x2+4xy=−x2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。 

而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。 

这款神奇游戏的每个关卡对 Kiana 来说都很难,所以 Kiana 还输入了一些神秘的指令,使得自己能更轻松地完成这个这个游戏。   

这些指令将在输入格式中详述。 

假设这款游戏一共有 TT 个关卡,现在 Kiana 想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。  

由于她不会算,所以希望由你告诉她。

注意:本题除 NOIP 原数据外,还包含加强数据。

输入格式

第一行包含一个正整数 T,表示游戏的关卡总数。

下面依次输入这 T 个关卡的信息。

每个关卡第一行包含两个非负整数 n,m,分别表示该关卡中的小猪数量和 Kiana 输入的神秘指令类型。

接下来的 nn 行中,第 ii 行包含两个正实数 (xi,yi),表示第 ii 只小猪坐标为 (xi,yi),数据保证同一个关卡中不存在两只坐标完全相同的小猪。

如果 m=0,表示 Kiana 输入了一个没有任何作用的指令。

如果 m=1,则这个关卡将会满足:至多用 ⌈n/3+1⌉ 只小鸟即可消灭所有小猪。

如果 m=2,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少 ⌊n/3⌋只小猪。

保证 1≤n≤18,0≤m≤2,0<xi,yi<10,输入中的实数均保留到小数点后两位。

上文中,符号 ⌈c⌉ 和 ⌊c⌋ 分别表示对 c 向上取整和向下取整,例如 :⌈2.1⌉=⌈2.9⌉=⌈3.0⌉=⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
​
#define x first
#define y second
​
using namespace std;
​
typedef pair<double, double> PDD;
​
const int N = 18, M = 1 << 18;
const double eps = 1e-8;
​
int n, m;
PDD q[N];
int path[N][N];
int f[M];
​
int cmp(double x, double y)
{
    if (fabs(x - y) < eps) return 0;
    if (x < y) return -1;
    return 1;
}
​
int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        cin >> n >> m;
        for (int i = 0; i < n; i ++ ) cin >> q[i].x >> q[i].y;
​
        memset(path, 0, sizeof path);
        for (int i = 0; i < n; i ++ )
        {
            path[i][i] = 1 << i;
            for (int j = 0; j < n; j ++ )
            {
                double x1 = q[i].x, y1 = q[i].y;
                double x2 = q[j].x, y2 = q[j].y;
                if (!cmp(x1, x2)) continue;
                double a = (y1 / x1 - y2 / x2) / (x1 - x2);
                double b = y1 / x1 - a * x1;
​
                if (cmp(a, 0) >= 0) continue;
                int state = 0;
                for (int k = 0; k < n; k ++ )
                {
                    double x = q[k].x, y = q[k].y;
                    if (!cmp(a * x * x + b * x, y)) state += 1 << k;
                }
                path[i][j] = state;
            }
        }
​
        memset(f, 0x3f, sizeof f);
        f[0] = 0;
        for (int i = 0; i + 1 < 1 << n; i ++ )
        {
            int x = 0;
            for (int j = 0; j < n; j ++ )
                if (!(i >> j & 1))
                {
                    x = j;
                    break;
                }
​
            for (int j = 0; j < n; j ++ )
                f[i | path[x][j]] = min(f[i | path[x][j]], f[i] + 1);
        }
​
        cout << f[(1 << n) - 1] << endl;
    }
​
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1254335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

显示器校准软件BetterDisplay Pro mac中文版介绍

BetterDisplay Pro mac是一款显示器校准软件&#xff0c;可以帮助用户调整显示器的颜色和亮度&#xff0c;以获得更加真实、清晰和舒适的视觉体验。 BetterDisplay Pro mac软件特点 - 显示器校准&#xff1a;可以根据不同的需求和环境条件调整显示器的颜色、亮度和对比度等参数…

Ansible的重用(include和import)

环境 管理节点&#xff1a;Ubuntu 22.04控制节点&#xff1a;CentOS 8Ansible&#xff1a;2.15.6 重用 Ansible提供四种可重用的工件&#xff1a; variable文件&#xff1a;只包含变量的文件task文件&#xff1a;只包含task的文件playbook&#xff1a;可包含play、变量、ta…

HTTP状态码:如何修复 404 Not Found错误?

互联网上各种类型的网站非常多&#xff0c;无论用户还是网站运营者不可避免的会遇到404 Not Found错误&#xff0c;如果遇到404错误&#xff0c;我们应该如何解决呢&#xff1f; 对于用户 检查拼写错误 如果您是遇到错误的用户&#xff0c;请仔细检查 URL 是否有任何拼写错误…

深入探索Linux文件系统:属性、路径与隐藏之谜

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Linux系统理论 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️文件的组成☁️文件属性☁️文件内容☁️注意事项 &#x1f324;️路…

JavaFX开发调用AWT创建系统托盘MenuItem菜单中文乱码

打开系统托盘MenuItem只能显示英文字符和中文显示方框 解决办法&#xff1a; 打开Edit Configurations… 选择Mofidy options 勾选Add VM options 在VM optios中填入以下代码 -Dfile.encodingGBK

【Docker】安装Redis 通俗易懂 亲测没有任何问题 只需三步

目录 1.配置redis.conf文件 2.下载镜像 3.运行容器 4.测试 1.配置redis.conf文件 以配置文件启动redis redis配置下载&#xff1a;Index of /releases/ 镜像最好和配置文件版本对应 避免一些不必要的错误 修改文件中以下配置项&#xff1a; 1.将bind 127.0.0.1 -::1注…

YOLOv8改进 | SAConv可切换空洞卷积(附修改后的C2f+Bottleneck)

论文地址&#xff1a;官方论文地址 代码地址&#xff1a;官方代码地址 一、本文介绍 本文给大家带来的改进机制是可切换的空洞卷积&#xff08;Switchable Atrous Convolution, SAC&#xff09;是一种创新的卷积网络机制&#xff0c;专为增强物体检测和分割任务中的特征提取而…

Flutter | TextField长按时选项菜单复制、粘贴显示为英文问题解决

Flutter | TextField长按时选项菜单复制、粘贴显示为英文问题解决 问题描述&#xff1a; 长按TextField后&#xff0c;显示剪切、复制等选项为英文&#xff0c;如下图所示&#xff0c;这是因为问未设置语言本地化&#xff0c;我们需要进行设置。 首先在pubspec.yaml加入以下依赖…

Dempster-Shafer(D-S)证据理论的基本定义和详细分析,优点,缺点,应用!!(系列1)

文章目录 前言一、D-S证据理论的应用&#xff1a;二、D-S证据理论的优点&#xff1a;三、D-S证据理论的缺陷&#xff1a;四、D-S组合规则&#xff1a;总结 前言 Dempster-Shafer&#xff08;D-S&#xff09;证据理论是一种不精确推理理论&#xff0c;也称为Dempster/Shafer证据…

BC76 [NOIP2008]ISBN号码

#include<stdio.h> int main() {char arr[13]; //存放13位的ISBNint i, j;scanf("%s",arr);int s 0;for(i0, j1; i<11; i){if(arr[i] ! -){s (arr[i]-0)*j; //将字符换成int累加&#xff1a;0162……29158j; //执行if的时候加&#xff0c;不执行不加…

P14 C++局部静态变量static延长生命周期

目录 01 前言 02 变量的作用域与生命周期 2.1 什么是作用域&#xff1a; 2.2 什么是变量的生命周期&#xff1a; 03 局部静态 3.1非静态变量例子 3.2静态变量例子 04 全局变量 05 后话 01 前言 在前几期里&#xff0c;我们了解了static关键字在特定上下文中的含义。 …

大厂工作的跨域架构师,最好掌握这4个原则

今天学习的主要内容如下&#xff1a; 什么是跨域架构师&#xff0c;在职责上跟单域架构师有什么区别&#xff1f;跨域架构师的勇气重要&#xff0c;还是技术重要&#xff1f;如何面对多领域之间的冲突&#xff0c;又如何解决冲突&#xff1f;我是一名跨域架构师&#xff0c;可以…

JavaScript 表达式

JavaScript 表达式 目录 JavaScript 表达式 一、赋值表达式 二、算术表达式 三、布尔表达式 四、字符串表达式 表达式是一个语句的集合&#xff0c;计算结果是个单一值。 在JavaScript中&#xff0c;常见的表达式有4种&#xff1a; &#xff08;1&#xff09;赋值表达式…

Xshell连接VMware虚拟机中的CentOS

Xshell连接VMware虚拟机中的CentOShttps://www.cnblogs.com/niuben/p/13157291.html 步骤&#xff1a; 1. 检查Linux虚拟机的网络连接模式&#xff0c;确保它是NAT模式。&#xff08;由于只在本机进行连接&#xff0c;所以没有选择桥接模式。当然&#xff0c;桥接模式的配置会…

中东客户亲临广东育菁装备参观桌面型数控机床生产

近日&#xff0c;中东地区的一位重要客户在广东育菁装备有限公司的热情接待下&#xff0c;深入了解了该公司生产的桌面型数控机床。这次会面不仅加强了双方在业务领域的交流&#xff0c;也为中国与中东地区的经济合作描绘出更美好的前景。 在育菁装备公司各部门主要负责人及工作…

[Java]线程详解

Java线程 一、线程介绍 程序 是为完成特定任务、用某种语言编写的一组指令的集合&#xff08;简单来说就是写的代码&#xff09;。 进程 进程是指运行中的程序&#xff0c;比如我们使用的QQ&#xff0c;就启动了一个进程&#xff0c;操作系统会对该进程分配内存空间。当我…

使用不平衡数据集练习机器学习

一、介绍 在当今世界&#xff0c;机器学习和人工智能几乎被广泛应用于每个领域&#xff0c;以提高绩效和结果。但如果没有数据&#xff0c;它们还有用吗&#xff1f;答案是否定的。机器学习算法严重依赖我们提供给它们的数据。我们提供给算法的数据质量在很大程度上决定了机器学…

LangChain 11实现思维树Implementing the Tree of Thoughts in LangChain’s Chain

思维之树&#xff08; Tree of Thoughts ToT&#xff09;是一个算法&#xff0c;它结合了普林斯顿大学和谷歌DeepMind在本文中提出的大型语言模型&#xff08;LLMs&#xff09;和启发式搜索。看起来这个算法正在被实现到谷歌正在开发的多模式生成AI Gemini中。 现在&#xff0…

vue2-009——mixin混入

一、基础使用案例 1.1、定义混合 1.2、引入混合 1.2.1、局部混入 1.2.2、全局引入 二、注意点 1、组件和混合有同名变量时&#xff0c;以组件的变量为主 2、组件和混合有同名生命周期钩子时&#xff0c;两者都会生效