《算法竞赛进阶指南》0x55 环形与后效性处理

2023-09-18 10:57:03

0x55 环形与后效性处理

休息时间

题意:

一天有 n n n 个小时,在第 i i i 个小时睡觉恢复体力 u i u_i ui。一头牛一天要休息 b b b 个小时,可以分成多段。每一段需要花费一个小时才能睡着,这一个小时不恢复体力。询问恢复体力的最大值。

解析:

可以考虑dp。第一维是每天的时间,第二维是已经休息的时间。转移的时候需要知道当前休息的这一个小时是否可以恢复体力,即是否是一段休息的第一个小时,所以增加第三维。

牛在每一个小时的状态有:清醒,入睡,熟睡。只有熟睡可以恢复体力,入睡和熟睡都是休息。

因为时间是环形的,所以可以通过分类讨论第一个小时的状态,来实现环形dp的转移

  • 第一个小时状态为清醒或者入睡
  • 第一个小时状态为熟睡,此时要求最后一个小时状态为入睡

注意空间大小,滚动数组优化空间

做题的时候一个不明白的地方是:对于第一种状态,第一个小时为入睡或者清醒,最后一个小时的状态是什么。其实清醒,入睡,熟睡都可以。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 4e3+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m, ans;
int a[maxn];
int f[2][maxn][2];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    memset(f, -INF, sizeof(f));
    int cur = 1, pre = 0;
    f[0][0][0] = f[0][1][1] = 0;
    for(int i = 2; i <= n; i++){
        for(int j = 0; j <= min(m, i); j++){
            f[cur][j][0] = max(f[pre][j][0], f[pre][j][1]);
            if(j)
                f[cur][j][1] = max(f[pre][j-1][0], f[pre][j-1][1]+a[i]);

        }
        cur ^= 1;
        pre ^= 1;
    }
    ans = max(f[pre][m][0], f[pre][m][1]);

    memset(f, -INF, sizeof(f));
    cur = 1, pre = 0;
    f[0][1][1] = a[1];
    for(int i = 2; i <= n; i++){
        for(int j = 0; j <= min(m, i); j++){
            f[cur][j][0] = max(f[pre][j][0], f[pre][j][1]);
            if(j)
                f[cur][j][1] = max(f[pre][j-1][0], f[pre][j-1][1]+a[i]);

        }
        cur ^= 1;
        pre ^= 1;
    }
    ans = max(ans, f[pre][m][1]);

    cout << ans << endl;
    return 0;
}


环路运输

题意:

环形公路上有 n n n 个仓库,仓库 i , j i,j i,j 之间的距离 d i s t ( i , j ) dist(i,j) dist(i,j) 为顺时针或者逆时针种较近的一种,在 i , j i,j i,j 运输的代价为 d i s t ( i , j ) + a i + a j dist(i,j) + a_i + a_j dist(i,j)+ai+aj。询问在两座仓库之间运送货物的最大代价

解析:

对于环形的处理是:断环为链。对于仓库 i , j ( j < i ) i,j (j < i) i,j(j<i) 运输,如果 i − j < N 2 i-j < \frac{N}{2} ij<2N,则在 i , j i,j i,j 之间运输;如果 i − j > N 2 i-j > \frac{N}{2} ij>2N,在 i , j + N i,j+N i,j+N 之间运输。

题目转化为:长为 2 n 2n 2n 的链,对于 i i i,找到 j j j,使 a i + i + a j − j a_i+i+a_j-j ai+i+ajj 最大,即 a j − j a_j-j ajj 最大 ( i − j < N 2 ) (i-j < \frac{N}{2}) (ij<2N)

可以用单调队列来维护 a j − j a_j-j ajj,时间复杂度为 O ( n ) O(n) O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define int ll
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e6+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, a[maxn << 1];
int q[maxn << 1], hh, tt;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		a[i+n] = a[i];
	}
	q[++tt] = 1;
	int len = n >> 1;
	int ans = 0;
	for(int i = 2; i <= n * 2; i++){
		while(hh <= tt && q[hh] < i-len)
			hh++;
		ans = max(ans, a[i]+i+a[q[hh]]-q[hh]);
		while(hh <= tt && a[q[tt]]-q[tt] < a[i]-i)
			tt--;
		q[++tt] = i;
	}
	cout << ans << endl;
	return 0;
}


坏掉的机器人

题意:

机器人初始在 ( x , y ) (x, y) (x,y),每步等概率向左、向右、原地不动、向下移动一格。求到 ( n , m ) (n,m) (n,m) 的步数的期望。

解析:

f i , j f_{i,j} fi,j ( i , j ) (i,j) (i,j) 走到 ( n , m ) (n,m) (n,m) 步数的期望

对于第一列: f i , 1 = 1 + 1 3 ( f i , 1 + f i + 1 , 1 + f i , 2 ) f_{i,1} = 1 + \frac{1}{3}(f_{i,1}+f_{i+1, 1}+f_{i, 2}) fi,1=1+31(fi,1+fi+1,1+fi,2)

对于第 2~m-1 列: f i , j = 1 + 1 4 ( f i , j + f i , j − 1 + f i , j + 1 + f i + 1 , j ) f_{i,j} = 1+\frac{1}{4}(f_{i,j}+f_{i, j-1}+f_{i, j+1}+f_{i+1, j}) fi,j=1+41(fi,j+fi,j1+fi,j+1+fi+1,j)

因为不能向上走,所以行间的转移没有后效性,可以线性转移。但行内相互转移,没有递推的顺序,所以需要解方程组,通过高斯消元来解方程组

注意:对于只有一列的情况, 1 2 \frac{1}{2} 21 停在原地, 1 2 \frac{1}{2} 21 向下走一步,所以期望是 2 ( n − x ) 2(n-x) 2(nx)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e3+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m, x, y;
double f[maxn][maxn], a[maxn][maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m >> x >> y;
	if(m == 1){
		cout << fixed << setprecision(4) << 2.0 * (n - x) << endl;
		return 0;	
	}	
	for(int i = n-1; i >= x; i--){
		a[1][1] = a[m][m] = 2 / 3.0, a[1][m + 1] = 1 + f[i + 1][1] / 3;
        a[1][2] = a[m][m - 1] = -1 / 3.0, a[m][m + 1] = 1 + f[i + 1][m] / 3;
        for(int j = 2; j <= m-1; j++){
        	a[j][j - 1] = a[j][j + 1] = -1 / 4.0;
			a[j][j] = 3 / 4.0, a[j][m + 1] = 1 + f[i + 1][j] / 4;
		}			
        for(int i = 1; i <= m; i++){
            double r = a[i + 1][i] / a[i][i];
            a[i + 1][i] = 0, a[i + 1][i + 1] -= r * a[i][i + 1];
			a[i + 1][m + 1] -= r * a[i][m + 1];
        }
        for(int j = m; j >= 1; j--) {
            double r = a[j - 1][j] / a[j][j];
            a[j - 1][j] = 0;
			a[j - 1][m + 1] -= a[j][m + 1] * r;
            f[i][j] = a[j][m + 1] / a[j][j];
        }
	}
	cout << fixed << setprecision(4) << f[x][y] << endl;
	return 0;
}

更多推荐

书剑宠物疫苗接种管理软件操作教程

【软件简介】书剑宠物疫苗接种管理软件是一款宠物疫苗接种管理的工具,适合宠物诊所使用。具有动物主人建档、宠物疫苗接种登记管理、每日提醒、打印疫苗接种通知卡、自定义短信提醒模板等完善的功能。另外本软件的特色是同时具有手机网页版功能,手机扫一扫即能接到电脑数据库,然后在手机上批量打电话,发短信,提醒疫苗接种即将到期的宠物主人

支持国密浏览器的堡垒机叫什么?联系电话多少?

支持国密浏览器的堡垒机叫什么?联系电话多少?目前市面上支持国密浏览器的堡垒机不多,这里给大家推荐行云管家堡垒机,其支持国密算法、国密浏览器,满足数据安全要求。行云管家堡垒机优势1、技术领先:技术团队实力雄厚,自主研发,原厂商;2、服务优质:提供24小时不间断的技术支持和服务;3、经验丰富:拥有10万+中小企业客户;4、

Command not found 解决方法

前言:要更新code上服务器用GUI失败,$patch_delivery_gui,报错:patch_delivery_gui:commandnotfound,上次编TA也是这个问题写了个脚本:这个脚本会先检查ifconfig、firewall-cmd和vim命令是否可用,如果不可用,则尝试安装相应的软件包。然后,它会显

从Vue 2到Vue 3:深入了解路由配置的变化与升级建议

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录📘前言vue2路由配置📟一、控制台安装vue路由📟二、项目src文件夹下创建router文件夹,并在router文件夹下创建index.js文件📟三、在index.js文件夹下进行vue路由配置📟四、

【云原生 | 56】Docker三剑客之Docker Swarm高效使用

🍁博主简介:🏅云计算领域优质创作者🏅2022年CSDN新星计划python赛道第一名🏅2022年CSDN原力计划优质作者🏅阿里云ACE认证高级工程师🏅阿里云开发者社区专家博主💊交流社区:CSDN云计算交流社区欢迎您的加入!目录1.创建集群id2.配置集群节点3.配置管理节点4.查看集群节点列表5.使用集群

软考 - 计算机组成与体系笔记

数据的表示进制转化二进制转十进制(十进制以D表示)从右往左,用二进制位上的数字乘以2的n次幂的和(n从0开始+1累加)十进制转二进制(二进制以B表示)十进制数不断除以2直至到0,得到的余数按从下而上的顺序排列得到的数值二进制与八进制(八进制以O或Q表示)二进制从右往左,每三位对应的都是八进制的一位数二进制与十六进制(十

LeetCode 753. 破解保险箱【欧拉回路,DFS】困难

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及

优化系统报错提示信息,提高人机交互(三)

对于业务比较复杂的接口,可能存在方法嵌套,每个方法都可能会报错,出现异常,那么需要把异常信息返回给接口调用者,如何实现呢?(1)捕获异常进行处理,不返回controller代码@AllArgsConstructor@RestController@Slf4jpublicclassDemoController{privat

Llama.cpp工具main使用手册

Llama.cpp提供的main工具允许你以简单有效的方式使用各种LLaMA语言模型。它专门设计用于与llama.cpp项目配合使用。推荐:用NSDT编辑器快速搭建可编程3D场景Llama.cpp的工具main提供简单的C/C++实现,具有可选的4位量化支持,可实现更快、更低的内存推理,并针对桌面CPU进行了优化。该程

【C++】构造函数初始化列表 ④ ( 构造函数 和 析构函数 调用顺序分析 )

文章目录一、构造函数和析构函数调用顺序说明1、构造函数调用顺序2、析构函数调用顺序3、拷贝构造函数也可以定义初始化列表二、构造函数和析构函数调用顺序代码分析1、构造函数调用顺序2、代码示例-构造/析构函数调用顺序分析构造函数初始化列表总结:初始化列表可以为类的成员变量提供初始值;初始化列表可以调用类的成员变量类型的构造

SpringBoot之静态资源规则与定制化

文章目录前言一、静态资源访问二、静态资源访问前缀三、webjar资源处理的默认规则四、welcome与favicon功能1.欢迎页支持欢迎页处理规则2.自定义Favicon五、补充总结前言本文主要介绍关于SpringBoot中Web开发的简单功能。一、静态资源访问只要静态资源放在类路径下:called/static(o

热文推荐