zoukankan      html  css  js  c++  java
  • 【题解】Acting Cute

    题目

    测试地址:POJ2228USACO 2005 January Gold

    题目来源:USACO 2005 January Gold / 模拟赛 T2(本题题面)。

    题目描述

    正在 rainbow 的城堡游玩的 Freda 恰好看见了在地毯上跳舞卖萌的水叮当……于是……

    Freda:“呜咕>_< 我也要卖萌T_T!”

    rainbow 给了 Freda (N) 秒的自由活动时间,不过由于刚刚游览城堡有些累了,Freda 只想花 (B) 秒的时间来卖萌,剩下的时间她要在 rainbow 的城堡里睡个好觉好好休息一下。

    rainbow 给这 (N) 秒每秒定义了一个值 (U_i),如果第 (i) 秒钟 Freda 在卖萌,那么她可以获得 (U_i) 点卖萌指数lala~

    Freda 开始卖萌后可以随时停止,休息一会儿之后再开始。不过每次 Freda 开始卖萌时,都需要 (1) 秒来准备= =,这一秒是不能获得卖萌指数的。当然,Freda 卖萌和准备的总时间 不能超过 (B)

    更特殊的是,这 (N) 秒钟时间是 环形 的。也就是 Freda 可以从任意时间开始她的自由活动并持续 (N) 秒。

    为了使自己表现得比水叮当更萌,现在 Freda 想知道,她最多能获得多少卖萌指数呢?

    输入格式

    第一行包含两个整数 (N)(B)

    (2)~(N+1) 行每行一个整数,其中第 (i+1) 行的整数表示 (U_i)

    输出格式

    输出一个整数,表示 Freda 可以获得的最大卖萌指数。

    数据范围

    各个测试点时间限制 (1mathrm{s}),空间限制 (1mathrm{GiB})

    • 对于 (60\%) 的数据,(Nleq 100)
    • 对于 (100\%) 的数据,(0leq Bleq Nleq 3600)(0leq U_ileq 200000)

    分析

    这道题可以看出来是 (mathtt{DP}),只不过环形这个条件比较难搞。

    线性情况

    状态定义和递推

    先考虑线性的情况,定义 (f_{i,j}) 为前 (i) 分钟已经卖萌 (j) 分钟的卖萌指数最大值。

    则可以得到:((val(i,j))代表从 (i) 时刻到 (j) 时刻的卖萌指数)

    [huge{f_{i,j} = max{f_{i-1,j}, max_{0< kleq j}{f_{i-k,j-k}+val(i-k+1,i)}}} ]

    在这个基础上,我们发现这个算法的复杂度是 (Theta(n^3)) 的,只能通过部分分。如何优化?

    优化

    首先,(val(i,j)) 是可以通过前缀和实现 (Theta(1)) 的(设前缀和数组为({pref_i}))。接下来考虑 (max_{0< kleq j}{f_{i-k,j-k}})

    考虑数列 (l_{k,u} = max_{kleq tleq u}{f_{t,t-k}-pref_{t+1}}),则 (max_{0< kleq j}{f_{i-k,j-k}}) 就是 (l_{i-j,i-1}+pref_i)

    每次计算出 (f_{i,j}) 后,可以 (Theta(1)) 更新 (l_{i-j,i}),这样就可以将复杂度降至 (Theta(n^2))

    同时 (l_{k,u}) 可以简化成 (l_k),节省空间。(虽然这道题有的是空间)

    上代码:

    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= i && j <= lim; j++)
        {
            dp[i][j] = ab_max(dp[i-1][j], ln[i-j] + pref[i]);
            if (i != n)
                ln[i-j] = ab_max(ln[i-j], dp[i-1][j] - pref[i+1]);
        }
    

    环形情况

    既然线性情况 Get,那么环形又如何处理呢?

    对于最优情况,首先考虑这几个结论:

    • 卖萌结束后的下一分钟,卖萌必然不开始。

    为什么?如果下一分钟开始了,这一分钟的卖萌指数就无法算入。但是如果将这一分钟连上上一分钟的卖萌,这一分钟的卖萌指数就能算入。

    考虑到 (U_i geq 0),所以算上一定 不差于 不算上。

    • 线性与环形的唯一差别就是第 (1) 分钟有没有卖萌 (不是准备)

    为什么?因为如果第 (1) 分钟没有卖萌或在做准备,则与线性的某种情况等价。

    唯一要考虑的环形情况就是有跨越的卖萌时间段。

    所以,我们再进行一遍 (mathtt{DP})。只不过这一次 (mathtt{DP}) 强制要求第 (1) 分钟正在卖萌,而且从某一时刻到第 (n) 分钟一直在卖萌(有准备)。

    为了思路清晰,这次使用 (revdp_{i,j}) 进行记录。

    上代码:

    memset(ln, 0xcf, sizeof(ln));
    revdp[0][0] = revdp[1][0] = 0;
    ln[0] = -pref[1]; // 注意初始化
    
    revdp[0][0] = 0;
    for (int i = 1; i <= lim; i++)
    	revdp[i][i] = pref[i];
    
    for (int i = 1; i < n; i++)
    	for (int j = 0; j < i && j <= lim; j++)
    	{
    		revdp[i][j] = ab_max(revdp[i-1][j], ln[i-j] + pref[i]);
    		ln[i-j] = ab_max(ln[i-j], revdp[i-1][j] - pref[i+1]);
    	}
    
    for (int i = 0; i <= lim; i++)
    	revdp[n][i] = ln[n-i] + pref[n];
    

    最后只需要在 (dp_{n,b})(revdp_{n,b}) 中选择一个最小值就行了。

    Code

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int max_n = 3600;
    
    int pref[max_n+1], dp[max_n+1][max_n+1], ln[max_n+1], revdp[max_n+1][max_n+1] = {};
    
    inline int ab_max(int x, int y) { return (x > y)? x:y; }
    
    int main()
    {
    	memset(dp, 0xcf, sizeof(dp));
    	memset(revdp, 0xcf, sizeof(revdp));
    	memset(ln, 0xcf, sizeof(ln));
    	dp[0][0] = dp[1][0] = 0;
    	
    	int n, lim, tmp;
    	
    	scanf("%d%d", &n, &lim);
    	
    	if (lim <= 1)
    	{
    		puts("0");
    		return 0;
    	}
    	
    	pref[0] = 0;
    	for (int i = 0; i < n; i++)
    	{
    		scanf("%d", &tmp);
    		pref[i+1] = pref[i] + tmp;
    	}
    	ln[0] = -pref[1];
    	
    	for (int i = 1; i <= n; i++)
    		for (int j = 0; j <= i && j <= lim; j++)
    		{
    			dp[i][j] = ab_max(dp[i-1][j], ln[i-j] + pref[i]);
    			if (i != n)
    				ln[i-j] = ab_max(ln[i-j], dp[i-1][j] - pref[i+1]);
    		}
    	
    	memset(ln, 0xcf, sizeof(ln));
    	revdp[0][0] = revdp[1][0] = 0;
    	ln[0] = -pref[1];
    	
    	revdp[0][0] = 0;
    	for (int i = 1; i <= lim; i++)
    		revdp[i][i] = pref[i];
    	
    	for (int i = 1; i < n; i++)
    		for (int j = 0; j < i && j <= lim; j++)
    		{
    			revdp[i][j] = ab_max(revdp[i-1][j], ln[i-j] + pref[i]);
    			ln[i-j] = ab_max(ln[i-j], revdp[i-1][j] - pref[i+1]);
    		}
    	
    	for (int i = 0; i <= lim; i++)
    		revdp[n][i] = ln[n-i] + pref[n];
    	
    	printf("%d
    ", ab_max(dp[n][lim], revdp[n][lim]));
    	
    	return 0;
    }
    

    备注

    标算使用的定义是 (f_{i,j,k}) 为前 (i) 分钟共卖萌 (j) 分钟,第 (i) 分钟的状态为 (k) 时的最大卖萌指数。

    则状态转移就是 (f_{i,j,k}=egin{cases}max_{u=0,1}{f_{i-1,j,u}}&k=0\max{f_{i-1,j-1,0},f_{i-1,j-1,1}+U_i}}&k=1end{cases})

    至于具体的细节,供读者思考。(明明是 5ab 太菜了,想不出来)

  • 相关阅读:
    web自动化测试---自动化脚本设置百度搜索每页显示条数
    web自动化测试---测试中其他一些常用操作
    web自动化测试---css方式定位页面元素
    web自动化测试---xpath方式定位页面元素
    linux系统 之 curl命令
    http协议
    php编程 之 php基础二
    shell编程 之 ssh远程连接
    php编程 之 php进阶练习
    php编程 之 php基础一
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-20200213-naptime.html
Copyright © 2011-2022 走看看