zoukankan      html  css  js  c++  java
  • Codeforces 1107E (Vasya and Binary String) (记忆化,DP + DP)

    题意:给你一个长度为n的01串,和一个数组a,你可以每次选择消除一段数字相同的01串,假设消除的长度为len,那么收益为a[len],问最大的收益是多少?

    思路:前两天刚做了POJ 1390,和此题很相似:POJ 1390 。我们甚至可以直接套用这个题的状态转移方程。仍然先把01串预处理一下,把相邻的并且数字相同的位合并成一个块。这样,01串就变成了若干个相邻的01块了。

    设dp[i][j][k]为处理第i个块到第j个块,并且后面有k个位和第j个块颜色相同,设f[i]为消除长度为i的串的最大收益,那么状态转移方程可以这样写:

    1:先把后面相同的合并了:dp[i][j][k] = max(dp[i][j][k], dp[i][j - 1][0] + f[第j个块的位的个数 + k]。

    2:与前面的颜色相同的块合并(假设块j与块p颜色相同):dp[i][j][k] = max(dp[i][j][k], dp[i][p][k + 第j个块的位的个数] + dp[p + 1][j - 1][0]);

    现在唯一的问题f数组怎么求?很明显此题并不是一次消除的长度越长越好,多次消除短的串可能获得更高的收益。我们可以思考一下,f[i]肯定是把长度i拆成1份,2份,......i份中收益最大的情况。

    设re[i][j]为把长度j拆成i份获得的最大收益,我们可以暴力枚举第i份的长度是多少(假设是k),然后继续递归去求得把j - k拆成i - 1份的最优值来更新当前状态。那么f[i]就是re[1][i],re[2][i].....re[i][i]中的最优情况。

    dp和re数组我们都可以记忆化,可以大幅度提高效率,31ms水过。。。

    代码:

    #include <bits/stdc++.h>
    #define LL long long
    using namespace std;
    LL dp[110][110][110];
    bool v[110][110][110], v1[110][110];
    LL f[110], b[110];
    LL re[110][110];
    
    struct node{
    	int flag, cnt;
    };
    node a[110];
    
    LL get_f(int tot, int n) {
    	if(v1[tot][n]) return re[tot][n];
    	if(tot == 1) {
    		v1[tot][n] = 1;
    		return re[tot][n] = f[n];
    	}
    	else {
    		LL ans = 0;
    		for (int i = 1; i <= n - tot + 1; i++) {
    			ans = max(ans, f[i] + get_f(tot - 1, n - i));
    		}
    		v1[tot][n] = 1;
    		return re[tot][n] = ans;
    	}
    }
    
    int cal(int n) {
    	LL ans = 0;
    	f[n] = b[n];
    	for (int i = 1; i <= n; i++) {
    		ans = max(ans, get_f(i, n));
    	}
    	f[n] = ans;
    }
    
    LL solve(int l, int r, int x) {
    	if(v[l][r][x]) return dp[l][r][x];
    	if(l >= r) return f[a[r].cnt + x];
    	LL ans = solve(l, r - 1, 0) + f[a[r].cnt + x];
    	for (int i = l; i < r; i++) {
    		if(a[i].flag == a[r].flag) {
    			ans = max(ans, solve(l, i, a[r].cnt + x) + solve(i + 1, r - 1, 0));
    		}
    	}
    	v[l][r][x] = 1;
    	return dp[l][r][x] = ans;
    }
    
    int main() {
    	int n;
    	char s[210];
    	scanf("%d", &n);
    	scanf("%s", s + 1);
    	int now_flag = -1, now = 0, tot = 0;
    	for (int i = 1; i <= n; i++) {
    		if(s[i] - '0' != now_flag) {
    			a[++tot].flag = s[i] -'0';
    			a[tot].cnt = 1;
    			now_flag = s[i] - '0';
    		} else {
    			a[tot].cnt++;
    		}
    	}
    	for (int i = 1; i <= n; i++) {
    		scanf("%lld", &b[i]);
    	}
    	for (int i = 1; i <= n; i++)
    		cal(i);
    	printf("%lld
    ", solve(1, tot, 0));
    } 
    

      

  • 相关阅读:
    MVC之路由规则 (自定义,约束,debug)
    MCV之行为
    mvc之页面强类型
    Jquery异步上传图片
    三层VS控制器
    Oracle 表分区
    C#编写的通过汉字得到拼音和五笔码
    MYSQL存储过程学习
    Sina App Engine(SAE)入门教程(9)- SaeMail(邮件)使用
    状态CSS
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10349329.html
Copyright © 2011-2022 走看看