刷题周记(八)——#区间DP:多边形、清空字符串 #状态机:股票买卖I~V、大盗阿福

——2020年12月13日(周日)——————————————————

#区间DP

一、多边形

这是题目链接
这道题思路和程序很快就搞定了,就是卡在了一个小细节上面:
最后比较的时候我居然还将最小值倒过来对比答案,可笑,又不是状态转移。
所以一定要清楚自己每一步在干什么,思路混乱时很可怕的。

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int n, a;
int maxf[N][N], minf[N][N];
char c[N];
int main(){
    memset(maxf, -0x3f, sizeof maxf);
    memset(minf, 0x3f, sizeof minf);
    cin >> n;
    for(int i = 1; i <= n; i ++){
    	//这里在%c前面加个空格是为了避免读入n后面的换行符
        scanf(" %c %d", &c[i], &a);
        c[i + n] = c[i];
        minf[i][i] = maxf[i][i] = minf[i + n][i + n] = maxf[i + n][i + n] = a;
    }
    
    for(int len = 2; len <= n; len ++){
        for(int i = 1; i + len - 1 <= 2 * n - 1; i ++){
            int j = i + len - 1;
            for(int k = i; k < j; k ++){
                if(c[k + 1] == 'x'){
                //既然写不对就写笨点
                    // maxf[i][j] = max(maxf[i][j], maxf[i][k] * maxf[k + 1][j]);
                    // maxf[i][j] = max(maxf[i][j], maxf[i][k] * minf[k + 1][j]);
                    // maxf[i][j] = max(maxf[i][j], minf[i][k] * maxf[k + 1][j]);
                    // maxf[i][j] = max(maxf[i][j], minf[i][k] * minf[k + 1][j]);
                    
                    // minf[i][j] = min(minf[i][j], maxf[i][k] * maxf[k + 1][j]);
                    // minf[i][j] = min(minf[i][j], minf[i][k] * maxf[k + 1][j]);
                    // minf[i][j] = min(minf[i][j], maxf[i][k] * minf[k + 1][j]);
                    // minf[i][j] = min(minf[i][j], minf[i][k] * minf[k + 1][j]);
                    maxf[i][j] = max(maxf[i][j], max( maxf[i][k] * maxf[k + 1][j], minf[i][k] * minf[k + 1][j] ) );
                    minf[i][j] = min(minf[i][j], min( maxf[i][k] * minf[k + 1][j], minf[i][k] * maxf[k + 1][j] ) );
                }
                    
                else{
                  maxf[i][j] = max(maxf[i][j], maxf[i][k] + maxf[k + 1][j]);
                  minf[i][j] = min(minf[i][j], minf[i][k] + minf[k + 1][j]);
                
                    // maxf[i][j] = max(maxf[i][j], maxf[i][k] + maxf[k + 1][j]);
                    // maxf[i][j] = max(maxf[i][j], maxf[i][k] + minf[k + 1][j]);
                    // maxf[i][j] = max(maxf[i][j], minf[i][k] + maxf[k + 1][j]);
                    // maxf[i][j] = max(maxf[i][j], minf[i][k] + minf[k + 1][j]);
                    
                    // minf[i][j] = min(minf[i][j], maxf[i][k] + maxf[k + 1][j]);
                    // minf[i][j] = min(minf[i][j], minf[i][k] + maxf[k + 1][j]);
                    // minf[i][j] = min(minf[i][j], maxf[i][k] + minf[k + 1][j]);
                    // minf[i][j] = min(minf[i][j], minf[i][k] + minf[k + 1][j]);
                    
                }
            }
        }
    }
    int maxl = -0x3f;
    for(int i = 1; i <= n; i ++)
        maxl = max(maxl, maxf[i][i + n - 1]);
    
    cout << maxl << endl;
    
    for(int i = 1; i <= n; i ++)
        if(maxf[i][i + n - 1] == maxl)
            printf("%d ", i);
    return 0;
}

——2020年12月14日(周一)——————————————————

——2020年12月15日(周二)——————————————————

二、清空字符串

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 610;
char s[N] , s1[N];
int n , ans , len;
//当前第i到第j这一串的最小的操作数是多少
int dp[N][N];
int main()
{
	memset(dp , 0x3f , sizeof dp);
	scanf("%d" , &len);
	scanf("%s" , s1 + 1);
	for(int i = 1 ; i <= len ;)
	{
		int j = i + 1;
		while(s1[i] == s1[j] && j <= len) j++;
		s[++n] = s1[i];
		i = j;
	}
	for(int i = 1 ; i <= n ; i++) dp[i][i] = 1;
	//得到新的字符串并初始化 
	for(int k = 1 ; k <= n ; k++)
	{
		for(int l = 1 ; l + k <= n ; l++)
		{
			int r = l + k;
			//先把头和尾独立开来进行操作
			dp[l][r] = min(dp[l + 1][r] , dp[l][r - 1]);
			//这里独立了头
			for(int j = l + 1 ; j < r ; j++)
				dp[l][r] = min(dp[l][r] , dp[l + 1][j] + dp[j + 1][r]);
			//这里独立了尾
			for(int j = l ; j < r - 1 ; j++)
				dp[l][r] = min(dp[l][r] , dp[l][j] + dp[j + 1][r - 1]);
			//因为肯定是先从中间开始进行合并的,所以最后只剩下头和尾,判断如果不相同那就数量++
			if(s[l] != s[r])
				dp[l][r]++;
//			cout << dp[l][r] << " ";
		}
//		cout << endl;
	}
	//我也不知道为什么要这样再找一次最小值,可能是有什么疏漏吧,反正不这么枚举的话会WA一个点。
	for(int j = 1 ; j < n ; j++)
		dp[1][n] = min(dp[1][n] , dp[1][j] + dp[j + 1][n]);
	int res = dp[1][n];
	printf("%d" , res);
	return 0;
}

——2020年12月16日(周三)——————————————————

——2020年12月17日(周四)——————————————————

——2020年12月18日(周五)——————————————————

#状态机

六个月前做的,由于没有留下笔记什么都不记得了……在此补上。

一、股票买卖I

题目

因为只能买入卖出一次,干脆就设为三个状态就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
//三种状态,什么也没买,已经买入,已经卖出
int f[N][3], a[N];
int main(){
    int n; cin >> n;
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    memset(f, -0x3f, sizeof f);
    f[0][0] = 0;
    for(int i = 1; i <= n; i ++){
        //什么也没买 只能由 什么也没买 转移过来 
        f[i][0] = f[i - 1][0];
        //已经买入 能由 什么也没买 或者 已经买入 转移过来
        f[i][1] = max(f[i - 1][0] - a[i], f[i - 1][1]);
        //已经买入 能由 已经买入 或者 已经卖出 转移过来
        f[i][2] = max(f[i - 1][1] + a[i], f[i - 1][2]);
    }
    if(f[n][2] < 0) printf("0");
    else cout << f[n][2];
    return 0;
}

这里是有股票买卖II的,但是从考察的状态来看,I 和 III 和 VI 题 是顺承下来的,状态类似的。II和V是状态类似的。
因此将II放到后面会好一点。便于参考对照。

二、股票买卖III

题目

注意的点:

第一,最多可以进行两次交易。那么可以进行一次交易,也可以进行两次。
第二,我们定义的状态中,maxk这一维的定义是:已经进行了k次交易,这里我们将买入次数算为交易次数,卖出不算,不然会重复。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int maxk = 3;
//最多进行k次交易的话,那么就会有k+1种不同的交易完成状态,包括没有交易。
int dp[maxn][maxk][2];
int w[maxn];
//这里k 定义的是最多进行交易的次数。后面由于最多只有两次就懒得用上k了。后面有更多次交易的话就要用上k进行枚举。
int n, k = 2;
int main(){
    memset(dp, -0x3f, sizeof(dp));
	cin >> n ;
	for(int i = 1; i <= n; i ++) cin >> w[i];
	//所有没有进行交易的都初始化为0;
	for(int i = 0; i <= n; i ++) dp[i][0][0] = 0; 
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= 2; j ++){
		//此时是手中有股,可以由 有股 和 没股 转移而来。
		//注意,我们定义的是已经交易了的次数,这里从没股到有股的过程是一定会消耗次数的,因此是dp[i - 1][j - 1][0] - w[i]
		dp[i][j][1] = max(dp[i - 1][j - 1][0] - w[i], dp[i - 1][j][1]);	
		//此时是手中没股,可以由 有股 和 没股 转移而来。
		//注意,这里是不会额外消耗一次交易次数的,因为这里还是算买入的那一次的。
		dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + w[i]);
	}
	int maxx = 0;
	maxx = max(maxx, max(dp[n][1][0], dp[n][2][0]) );
	cout << maxx << endl;
    
	return 0;
}

三、股票买卖IV

题目

由于是承接着第三题的,很显然最大交易次数k不再是定量2而是更麻烦的变量了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int maxk = 110;
//最多进行k次交易的话,那么就会有k+1种不同的交易完成状态,包括没有交易。
int dp[maxn][maxk][2];
int w[maxn];
//这里k就用上了。
int n, k ;
int main(){
    memset(dp, -0x3f, sizeof(dp));
	cin >> n >> k;
	for(int i = 1; i <= n; i ++) cin >> w[i];
	//所有没有进行交易的都初始化为0;
	for(int i = 0; i <= n; i ++) dp[i][0][0] = 0; 
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= k; j ++){
		dp[i][j][1] = max(dp[i - 1][j - 1][0] - w[i], dp[i - 1][j][1]);	
		dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + w[i]);
	}
	int maxx = -1;
	for(int i = 0; i <= k; i ++) maxx = max(maxx, dp[n][i][0]);
	cout << maxx << endl;
    
	return 0;
}

–分割线-- ------

接下来这种状态会好理解很多。

四、股票买卖II

题目

同样是只有一支股票,但是可以进行多次交易,那就分为手中有股和手中没股两种状态。
重点是多次交易,没有限制次数的话,表示状态时从手中有、无股票出发就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int dp[2][N], a[N], n;
int main(){
    memset(dp, -0x3f, sizeof dp);
	cin >> n;	
	for(int i = 1; i <= n; i ++) cin >> a[i];
	dp[0][0] = 0;
	for(int i = 1; i <= n; i ++){
	    //没股 可以由 没股 以及 有股 转移过来
		dp[0][i] = max(dp[0][i - 1], dp[1][i - 1] + a[i]);
		//有股 可以由 没股 和 有股 转移过来
		dp[1][i] = max(dp[0][i - 1] - a[i], dp[1][i - 1]);	
	}
	//很明显最后手中没股的话应该会比有股大吧,买入股不用钱啊?买入之后不卖出 还比 不买入 还大怎么可能啊。
	cout << dp[0][n];
	return 0;
}

五、股票买卖V

题目

多了个冷冻期,那么显然就有三种状态:有股 没股 以及 冷冻期中。

#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, k, f[N][3], w[N];
int main(){
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> w[i];
    memset(f, 0xcf, sizeof(f));
    f[0][0] = 0;
    for(int i = 1; i <= n; i ++){
    	//没股
        f[i][0] = max(f[i - 1][2], f[i - 1][0]);
        //有股
        f[i][1] = max(f[i - 1][1], f[i - 1][0] - w[i]);
        //冷冻期
        f[i][2] = f[i - 1][1] + w[i];
    }
    cout << max(f[n][0], f[n][2]);
    return 0 ;
}

六、股票买卖VI

题目

相比于第四题,多了个手续费,每两次操作会消耗一次手续费,即一次买入一次卖出只会消耗一次手续费。
根据III中的结论,在买入或者卖出的转移减去手续费就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int dp[2][N], a[N], n, f;
int main(){
    memset(dp, -0x3f, sizeof dp);
	cin >> n >> f;	
	for(int i = 1; i <= n; i ++) cin >> a[i];
	dp[0][0] = 0;
	for(int i = 1; i <= n; i ++){
	    //没股 可以由 没股 以及 有股 转移过来
		dp[0][i] = max(dp[0][i - 1], dp[1][i - 1] + a[i] - f);
		//有股 可以由 没股 和 有股 转移过来
		dp[1][i] = max(dp[0][i - 1] - a[i], dp[1][i - 1]);	
	}
	cout << dp[0][n];
	return 0;
}

——2020年12月19日(周六)——————————————————

大盗阿福

网卡了一下没保存……白写了。
题目

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int f[N][2], w[N];
int main(){
    int t; cin >> t;
    while(t --){
        int n; cin >> n;
        memset(f, -0x3f, sizeof f);
        f[0][0] = 0;
        for(int i = 1; i <= n;i ++) cin >> w[i];
        for(int i = 1; i <= n;i ++){
            //不打劫这一家店
            f[i][0] = max(f[i - 1][0], f[i - 1][1]);
            //打劫这一家店只能是从不打劫上一家店转移过来
            f[i][1] = f[i - 1][0] + w[i];
        }
        printf("%d\n", max(f[n][1], f[n][0]) );
    }
    return 0;
}

设计密码

题目

——(完)——————————————————

To be continue……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值