• poj_1390 动态规划


    题目大意

        将一些连续的序列根据颜色分为N段,每段有颜色 为 Ci, 长度为 Li。每次点击其中的一段 i ,则可以将该段i消除,该段相邻的两段自动连接到一起,如果连接到一起的两段之前的颜色相同,则更新该段的长度。消除过程可以得到得分 Li*Li。求当所有的段都消除完毕时的最多得分。

    分析

        求最优化问题,典型的动态规划。 
    (1)考虑前n个段被消除最大得分,求dp[n]? 
        显然不行,因为无法保证无后效性,因为这种递推从前向后,如果先消除了前i个段,则前i个段中可能和后面的段合并后再消除的情况就不存在了,即无法表示所有的情况。这样就导致无法构成递推关系,即即使知道了dp[0], dp[1], ....dp[n-1], 也无法知道 dp[n]; 
    (2)考虑 段i到段j之间被消除的最大得分,求 dp[0][n-1]? 即区间动态规划 
        既然要求完全消除 [i...j] 的得分,则必定要先消除一个,就从最后一个段j开始。此时有两种选择: 
            1、直接消除j,得分Lj*Lj. 
            2、先消除j之前的某些段,遇到一个颜色和j相同的段,将该段和j进行合并(由于j之前可能有多个间隔的和j颜色相同的段s,因此需要进行枚举取最大值) 
        但是,在第二种选择中,将j和某个段s进行合并之后,得到一个新的段,此时是直接消除该大段,还是继续选择将该大段和之前的某个段s1合并后再消除? 
        如果需要构造递推关系,则有 dp[i][j] = max(dp[i][j], dp[i][s'] + dp[s+1][j-1]) 
    其中 dp[s+1][j-1]是将 s到j之间的先消除,将s和j进行合并,设s和j合并后得到的段为s',dp[i][s']是将i到 s'进行合并后的得分。 
        这样做,明显会改变原来的段的大小结构,多个段进行交叉操作时会乱,无法保证无后效性。

    (3)进一步考虑: 
    dp数组,dp[i][j][k] 表示从下i到j的连续的段,且j之后有长度为k的颜色和 Seq[j]相同,但是不和j相邻的段, 
    将 [i, j] 内的段完全消除,且将j之后的那长度为k的段也消除,能够得到的最大分数 
    注意,消除的时候,分值不包括 j到长度为k的段之间的那些数据消除的得分

        这样构造状态,就可以形成递推关系: 
    dp[i][j][k] = dp[i][j-1][0] + (k+Lj)*(k+Lj) 
    dp[i][j][k] = max(dp[i][j][k], dp[i][s][k + Lj] + dp[s+1][j-1][0])

    实现(c++)

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<string.h>
    #define MAX_SEG_NUM 202
    
    //保存每段的数据,颜色和长度
    struct Seg{
    	int color;
    	int length;
    };
    Seg gSegs[MAX_SEG_NUM];
    //dp数组,dp[i][j][k] 表示从下i到j的连续的段,且j之后有长度为k的颜色和 Seq[j]相同,但是不和j相邻的段,
    //将 [i, j] 内的段完全消除,且将j之后的那长度为k的段也消除,能够得到的最大分数
    //注意,消除的时候,分值不包括 j到长度为k的段之间的那些数据消除的得分
    int dp[MAX_SEG_NUM][MAX_SEG_NUM][MAX_SEG_NUM];
    
    int Square(int x){
    	return x*x;
    }
    //dp函数。采用递归式的动态规划
    int GetScore(int start, int end, int ex_len){
    	//如果已经求完,则直接返回
    	if (dp[start][end][ex_len])
    		return dp[start][end][ex_len];
    
    	int result = Square(gSegs[end].length + ex_len); //将段j和j之后长度为k的段进行合并,直接消除求出结果
    	if (start == end)	//递归结束
    		return dp[start][end][ex_len] = result;
    
    	result += GetScore(start, end - 1, 0); 
    	//方案一:将段j和j之后长度为k的段进行合并消除,然后加上 i 到j 之间的段进行求和(注意 ex_len 为0是因为
    	//在求 dp[i][j][k]的时候,分值不包括 j到长度为k的段之间的那些数据的得分,因此 求 i到j-1之间的消除得分即可,即 ex_len = 0
    
    
    	//方案二:在将j和j之后长度为k的段放在一块之后,先不消除,而是递归到子问题:
    	//从i到j之间找出s,使得s的颜色和j的颜色相同,然后先消去 s+1到j-1 之间的部分SS,则子问题 为 dp[i][s][length(j) + k] + SS
    	//从 SS中取出最大值
    	for (int i = end - 1; i >= start; i--){
    		if (gSegs[i].color != gSegs[end].color)
    			continue;
    		int tmp = GetScore(start, i, ex_len + gSegs[end].length) + GetScore(i + 1, end - 1, 0);
    		if (tmp > result)
    			result = tmp;
    	}
    	return dp[start][end][ex_len] = result;
    }
    int main(){
    	int cas, c = 1;
    	int total_num, color, last_color, k;
    	scanf("%d", &cas);
    	while (c <= cas){
    		scanf("%d", &total_num);
    		last_color = -1;
    		k = -1;
    		memset(dp, 0, sizeof(dp));
    		for (int i = 0; i < total_num; i++){
    			scanf("%d", &color);
    			if (last_color != color){
    				k++;
    				last_color = color;
    				gSegs[k].length = 0;
    				gSegs[k].color = color;
    			}
    			gSegs[k].length++;
    		}
    
    		printf("Case %d: %d
    ", c++, GetScore(0, k, 0));
    	}
    	return 0;
    }
    
  • 相关阅读:
    设计模式的原则和法则
    GoF的23种设计模式分类和功能
    2020年智慧电力解决方案
    【转载】「黑科技」智能防疫消毒机器人 技术方案介绍-disinfection robot
    【转载】如何让电力巡检机器人项目落地
    30多张图来了解Keil5的使用
    [数学学习与代码]最小二乘法--多元线性方程求解
    MTK-LCM 屏幕使用fbconfig/PanelMaster来调试LCM驱动
    MTK 使用iptable 命令来完成网络路由(android WIFI/4G分享网络)
    MTK(android init.rc) 写一个开机启动的服务
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4781860.html
Copyright © 2020-2023  润新知