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

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值