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


    0x55 环形与后效性处理

    休息时间

    题意:

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

    解析:

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

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

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

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

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

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

    代码:

    #include
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57


    环路运输

    题意:

    环形公路上有 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
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40


    坏掉的机器人

    题意:

    机器人初始在 ( 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
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
  • 相关阅读:
    基于B2B平台的医疗病历交互系统
    c4d里oc摄像机景深能不能只模糊近处不影响远景
    运筹学基础【五】 之 线性规划
    算法----字符串转换整数 (atoi)
    【康师傅】MySQL事务
    什么是泄放电阻器:您应该知道的 11 个重要事实?
    线上问题——学习记录幂等判断失效问题分析
    什么是springMVC 视图和视图解析器
    Python数据容器(字符串)
    【零基础学Python】运算符
  • 原文地址:https://blog.csdn.net/m0_53899788/article/details/132963364