zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:优化(贪心+DP)

    题目描述

    $visit ext{_}world$发现有下优化问题可以用很平凡的技巧解决,所以他给你分享了这样一道题:
    现在有长度为$N$的整数序列${ a_i}$,你需要从中选出$K$个不想叫的连续子区间(可以存在元素不被选),从左到右记它们的和为$s_1,s_2,...,s_k$,我们的优化目标是最大化下述和式:
    $$sum limits_{i=1}^{k-1}|s_i-s_{i+1}|$$
    你只需要输出这个最大的和即可。


    输入格式

    第一行两个整数$N,K$,意义如上。
    接下来一行$N$个整数,第$i$个数表示$a_i$,保证有$|a_i|leqslant 10^4$。


    输出格式

    输出一行一个整数,表示答案。


    样例

    样例输入:

    5 3
    5 2 4 3 1

    样例输出:

    12


    数据范围与提示

    样例解释:

    选择$(5),(2,4,3),(1)$三个子段,$|5-9|+|9-1|=12$

    数据范围:

    对于全部的测试数据,保证$Nleqslant 3 imes 10^4,Kleqslant min(N,200)$。
    $ullet$子任务$1$($10$分):$Kleqslant 3$。
    $ullet$子任务$2$($30$分):$Nleqslant 400,Kleqslant 50$。
    $ullet$子任务$3$($20$分):$Nleqslant 10^3,Kleqslant 100$。
    $ullet$子任务$4$($40$分):无特殊限制。


    题解

    利用贪心思想,这$k$个序列一定是一高一低的,不可能连续三个及以上持续上升或下降。

    对于高的序列,其贡献为$+2$;对于低的序列,其贡献为$-2$;而中间也会有一些并不选的状态,其贡献为$0$,而对于这些状态,其接下来会有高或低的序列,为了区分它们,不妨将其称为上升状态$or$下降状态。

    考虑$DP$,设$dp[i][j][0/1/2/3]$表示前$i$个数,分成了$j$段,当前状态是高、低、上升、下降状态。

    状态转移很简单,需要注意的是对于边界的处理,即$j=1$和$j=K$的情况。

    时间复杂度:$Theta(NK)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int N,K;
    int w[30001];
    int dp[30001][201][4];
    int main()
    {
    	scanf("%d%d",&N,&K);
    	for(int i=1;i<=N;i++)
    		scanf("%d",&w[i]);
    	memset(dp,-0x3f,sizeof(dp));
    	for(int i=1;i<=N;i++)dp[i][0][0]=dp[i][0][1]=dp[i][0][2]=dp[i][0][3]=0;
    	dp[0][0][0]=dp[0][0][1]=dp[0][0][2]=dp[0][0][3]=0;
    	for(int i=1;i<=N;i++)
    	{
    		dp[i][1][0]=max(dp[i-1][1][0],dp[i-1][0][2])+w[i];
    		dp[i][1][1]=max(dp[i-1][1][1],dp[i-1][0][3])-w[i];
    		dp[i][1][2]=max(dp[i-1][1][2],dp[i][1][1]);
    		dp[i][1][3]=max(dp[i-1][1][3],dp[i][1][0]);
    		dp[i][K][0]=max(dp[i-1][K][0],dp[i-1][K-1][2])+w[i];
    		dp[i][K][1]=max(dp[i-1][K][1],dp[i-1][K-1][3])-w[i];
    		dp[i][K][2]=max(dp[i-1][K][2],dp[i][K][1]);
    		dp[i][K][3]=max(dp[i-1][K][3],dp[i][K][0]);
    		for(int j=2;j<K;j++)
    		{
    			dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j-1][2])+2*w[i];
    			dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][3])-2*w[i];
    			dp[i][j][2]=max(dp[i-1][j][2],max(dp[i][j][1],dp[i-1][j-1][2]));
    			dp[i][j][3]=max(dp[i-1][j][3],max(dp[i][j][0],dp[i-1][j-1][3]));
    		}
    	}
    	printf("%d",max(dp[N][K][2],dp[N][K][3]));
    	return 0;
    }
    

    rp++

  • 相关阅读:
    金蝶k3wise 核算项目、辅助资料
    金蝶——“免、抵、退”税操作说明及帐务处理
    阿里云各Linux发行版netcore兼容性评估报告---来自大石头的测试
    金蝶KIS&K3助记码SQL数据库批量刷新
    华为交换机批量加入 Vlan 方法
    华为设备默认console密码
    SQL查询数据并插入新表
    ORACLE删除当前用户下所有的表的方法
    【转】使用Navicat for Oracle新建表空间、用户及权限赋予
    [转]spring mvc注解方式实现向导式跳转页面
  • 原文地址:https://www.cnblogs.com/wzc521/p/11743598.html
Copyright © 2011-2022 走看看