动态规划之区间dp(题集)

目录

1.被3整除的子序列

2.Eating Together

3.Basketball Exercise:

4.RGB Substring

5.小明打联盟

6.乘积最大

7.Make The Fence Great Again


1.被3整除的子序列

题目描述

给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除
答案对1e9+7取模

输入描述:

输入一个字符串,由数字构成,长度小于等于50

输出描述:

输出一个整数

输入

复制

132

输出

复制

3

备注:

n为长度
子任务1: n <= 5
子任务2: n <= 20
子任务3: 无限制

链接:https://ac.nowcoder.com/acm/problem/21302

解题思路:能够被3整除,首先要知道某个数可以被3整除,那么这个数每一位加起来可以被3整除,比如12,1+2=3可被整除。那么接下来将问题转成:对string进行遍历,每出现一位新数字,对上一结果造成什么样影响?并统计a[i]%3的结果。

a[i]%3=0 (说明存在一个可被3整除)

a[i]%3=1(如果接下来再来个2,也可被3整除)

a[i]%3=2(如果接下来再来个1,也可被3整除)

因为是子序列,在任意区段内可以随意组合数字,所以每次往后for新状态就要继承原来状态,并且当前a[i]的加入,可为上个区段带来什么样新结果(求余可产生新0,1,2个数)

比如:321

3的加入,3%3=0

dp[i][0]=dp[i-1][0]+dp[i-1][0]+1;(再此之前有dp[i-1][0]个可被整除,加入之后会产生新的组合个dp[i-1][0]个,在加上本身1个)

dp[i][1]=dp[i-1][1]+dp[i-1][1];(之前有dp[i-1][1]个和求余为1,加入0之后0+1=1,产生新的组合中求余1:dp[i-1][0]个,再相加)

dp[i][2]=dp[i-1][2]+dp[i-1][2];(之前求余2有dp[i-1][2],加入0后0+2=2,新组合求余为2有dp[i-1][2],dp[i][2]=之前数量+当前状态数量)

2的加入,2%3=2

dp[i][0]=dp[i-1][0]+dp[i-1][1];

dp[i][1]=dp[i-1][1]+dp[i-1][2];

dp[i][2]=dp[i-1][2]+dp[i-1][0]+1;

1的加入,1%3=1

dp[i][0]=dp[i-1][0]+dp[i-1][2];

dp[i][1]=dp[i-1][1]+dp[i-1][0]+1;

dp[i][2]=dp[i-1][2]+dp[i-1][1]+1;

#include <iostream>
using namespace std;
#define mod 1000000007
long long dp[55][3];
int main()
{
	string a;
	cin>>a;
	for(int i=0;i<a.length();i++)
	{
		int j=i+1;
		int x=a[i]-'0';
		if(x%3==0)
		{
			dp[j][0]=dp[j-1][0]+dp[j-1][0]+1;
			dp[j][1]=dp[j-1][1]+dp[j-1][1];
			dp[j][2]=dp[j-1][2]+dp[j-1][2];
		}
		if(x%3==1)
		{
			dp[j][0]=dp[j-1][2]+dp[j-1][0];
			dp[j][1]=dp[j-1][0]+dp[j-1][1]+1;
			dp[j][2]=dp[j-1][1]+dp[j-1][2];
		}
		if(x%3==2)
		{
			dp[j][0]=dp[j-1][1]+dp[j-1][0];
			dp[j][1]=dp[j-1][2]+dp[j-1][1];
			dp[j][2]=dp[j-1][0]+dp[j-1][2]+1;
		}
	}
	cout<<dp[a.length()][0]%mod;
} 

2.Eating Together

题目描述

The cows are so very silly about their dinner partners. They have organized themselves into three groups (conveniently numbered 1, 2, and 3) that insist upon dining together. The trouble starts when they line up at the barn to enter the feeding area.
Each cow i carries with her a small card upon which is engraved Di (1 ≤ Di ≤ 3) indicating her dining group membership. The entire set of N (1 ≤ N ≤ 30,000) cows has lined up for dinner but it's easy for anyone to see that they are not grouped by their dinner-partner cards.
FJ's job is not so difficult. He just walks down the line of cows changing their dinner partner assignment by marking out the old number and writing in a new one. By doing so, he creates groups of cows like 111222333 or 333222111 where the cows' dining groups are sorted in either ascending or descending order by their dinner cards.
FJ is just as lazy as the next fellow. He's curious: what is the absolute mminimum number of cards he must change to create a proper grouping of dining partners? He must only change card numbers and must not rearrange the cows standing in line.

输入描述:

* Line 1: A single integer: N
* Lines 2..N+1: Line i describes the i-th cow's current dining group with a single integer: Di

输出描述:

* Line 1: A single integer representing the minimum number of changes that must be made so that the final sequence of cows is sorted in either ascending or descending order

输入

5
1
3
2
1
1

输出

1

说明

We would need at least two changes to turn this into an increasing sequence (changing both non-1's to a 1).
However, changing the first "1" to a "3" yields a decreasing sequence with just one change, which is optimal.

链接:https://ac.nowcoder.com/acm/contest/997/G

题意:将给的数组变换m个数,变成上升数组或者下降数组,求最少需要几次m。

#include <iostream>
#include <queue>
#include <cmath>
#define INF 0x3f3f3f3f
using namespace std;
int sh[30005][4];
int ji[30005][4];
int a[30005];
int n;
int read()
{
    char ch=getchar();
	long long x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
void solve()
{
	for(int i=1;i<=n;i++)
	{
		if(a[i]==1)
		{
			sh[i][1]=sh[i-1][1];
			sh[i][2]=min(sh[i-1][1],sh[i-1][2])+1;
			sh[i][3]=min(sh[i-1][1],min(sh[i-1][2],sh[i-1][3]))+1;
			
			ji[i][1]=min(ji[i-1][1],min(ji[i-1][2],ji[i-1][3]));
			ji[i][2]=min(ji[i-1][2],ji[i-1][3])+1;
			ji[i][3]=ji[i-1][3]+1;
		}
		else if(a[i]==2)
		{
			sh[i][1]=sh[i-1][1]+1;
			sh[i][2]=min(sh[i-1][1],sh[i-1][2]);
			sh[i][3]=min(sh[i-1][1],min(sh[i-1][2],sh[i-1][3]))+1;
			
			ji[i][1]=min(ji[i-1][1],min(ji[i-1][2],ji[i-1][3]))+1;
			ji[i][2]=min(ji[i-1][2],ji[i-1][3]);
			ji[i][3]=ji[i-1][3]+1;
		}
		else if(a[i]==3)
		{
			sh[i][1]=sh[i-1][1]+1;
			sh[i][2]=min(sh[i-1][1],sh[i-1][2])+1;
			sh[i][3]=min(sh[i-1][1],min(sh[i-1][2],sh[i-1][3]));
			
			ji[i][1]=min(ji[i-1][1],min(ji[i-1][2],ji[i-1][3]))+1;
			ji[i][2]=min(ji[i-1][2],ji[i-1][3])+1;
			ji[i][3]=ji[i-1][3];
		}
	}
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	solve();
	cout<<min(min(sh[n][1],min(sh[n][2],sh[n][3])),min(ji[n][1],min(ji[n][2],ji[n][3])));
}

3.Basketball Exercise:

链接:http://codeforces.com/contest/1195/problem/C

题意:上下选一个,可以不选,左右不能相邻,结果总身高最大。

/*时间:2019.07.18*/
#include<iostream>
using namespace std;
long long dp[100010][3];
long long a[100010][3];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
	{
        scanf("%lld",&a[i][0]);
    }
    for(int i=1;i<=n;i++)
	{
        scanf("%lld",&a[i][1]);
    }
    dp[0][0]=dp[0][1]=dp[0][2]=0;
    for(int i=1;i<=n;i++)
	{
        dp[i][0]=max(max(dp[i-1][0],dp[i-1][1]),dp[i-1][2]);
        dp[i][1]=max(dp[i-1][0],dp[i-1][2])+a[i][0];
        dp[i][2]=max(dp[i-1][0],dp[i-1][1])+a[i][1];
    }
    cout<<max(max(dp[n][0],dp[n][1]),dp[n][2])<<endl;
    return 0;
}

4.RGB Substring

The only difference between easy and hard versions is the size of the input.

You are given a string ss consisting of nn characters, each character is 'R', 'G' or 'B'.

You are also given an integer kk. Your task is to change the minimum number of characters in the initial string ss so that after the changes there will be a string of length kk that is a substring of ss, and is also a substring of the infinite string "RGBRGBRGB ...".

A string aa is a substring of string bb if there exists a positive integer ii such that a1=bia1=bi, a2=bi+1a2=bi+1, a3=bi+2a3=bi+2, ..., a|a|=bi+|a|−1a|a|=bi+|a|−1. For example, strings "GBRG", "B", "BR" are substrings of the infinite string "RGBRGBRGB ..." while "GR", "RGR" and "GGG" are not.

You have to answer qq independent queries.

input

3

5 2

BGGGG

5 3

RBRGR

5 5

BBBRR

output

1

0

3

题链接:https://codeforces.com/contest/1196/problem/D2

题意:给一段长度为n的string,可以修改每个字母,找到一段长度为k的字串,该字串是“RGBRGBRGBRGB...”的字串。

#include<iostream>
#include<cstring> 
#define INF 0x3f3f3f3f
using namespace std;
int n,k;
string s;
int solve()
{
	int minn=INF;
	int opt[n+5][4]={0};//r 1 g 2 b 3
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='R')
		{
			opt[i][1]=opt[i-1][3];
			opt[i][2]=opt[i-1][1]+1;
			opt[i][3]=opt[i-1][2]+1;
		}
		else if(s[i]=='G')
		{
			opt[i][1]=opt[i-1][3]+1;
			opt[i][2]=opt[i-1][1];
			opt[i][3]=opt[i-1][2]+1;
		}
		else if(s[i]=='B')
		{
			opt[i][1]=opt[i-1][3]+1;
			opt[i][2]=opt[i-1][1]+1;
			opt[i][3]=opt[i-1][2];
		}
	}
	int top=k%3;
	int dp[n+5][4]={0};
	for(int i=k;i<=n;i++) 
	{
		if(top==0)
		{
			dp[i][1]=opt[i][1]-opt[i-k][1];
			dp[i][2]=opt[i][2]-opt[i-k][2];
			dp[i][3]=opt[i][3]-opt[i-k][3];
		}
		else if(top==1)
		{
			dp[i][1]=opt[i][1]-opt[i-k][3];
			dp[i][2]=opt[i][2]-opt[i-k][1];
			dp[i][3]=opt[i][3]-opt[i-k][2];
		}
		else if(top==2)
		{
			dp[i][1]=opt[i][1]-opt[i-k][2];
			dp[i][2]=opt[i][2]-opt[i-k][3];
			dp[i][3]=opt[i][3]-opt[i-k][1];
		}
		minn=min(min(dp[i][1],dp[i][2]),min(dp[i][3],minn));
	}
	return minn;
}
 
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&k);
		cin>>s;
		s="0"+s;
		cout<<solve()<<endl;
	}
	return 0;
}
/*
20 3
GRRRGRGGRBGRRGBRRBGG
1
12 3
RBGRRGBRRBGG
*/

5.小明打联盟

题目描述

小明很喜欢打游戏,现在已知一个新英雄即将推出,他同样拥有四个技能,其中三个小技能的释放时间和固定的伤害值为:

1.乌鸦坐飞机 释放时间:x 固定伤害值:a

2.蜘蛛吃耳屎 释放时间:y 固定伤害值:b

3.饿狼前进  释放时间:z 固定伤害值:c

他还有一个大招,其释放的时间是一个区间【L,R】,可以在区间内任意时间点释放出技能,其如果在L+i时刻释放技能,其能够打出的伤害值为:temp+A*i

这里temp值表示技能的基础伤害(同时也就是在时刻L释放技能的伤害值),A是一个常数。

小明很喜欢研究连招使得在有限的时间内打出最高的伤害,现在他想要在T长度单位时间内打出最高的伤害,问这个最大伤害值。

输入描述:

本题包含多组数据。
输入格式为:
T
x a
y b
z c
L R temp A
数据范围:
1<=T<=1e5
1<=x,y,z,L,R<=T
L<=R
<=a,b,c,temp,A<=1e5

输出描述:

输出包含一行,输出能够打出的最高伤害值。

输入

8
3 1
2 3
1 3
3 3 3 3
输出
24
备注:
大招:蓄力时间最短L秒,最多R秒。无限次释放,释放之后照成的伤害是随着时间增加的
蓄力L秒释放能够造成Temp的伤害
蓄力L+1秒释放能够造成Temp+1*A的伤害

链接:https://ac.nowcoder.com/acm/problem/14553

题意:技能不能叠加,T时间内输出最大伤害,大招在L~R时间内变化为Temp+(j-L)*A

题解:L~R范围太大,dp肯定要从这里下手,通过大招斜率,手推关系,发现大招要么选L 要么选R。

#include<iostream>
using namespace std;
int main()
{
    int T;
    while(scanf("%d",&T)!=EOF)
	{
	    int  w[5];
	    long long v[5];
	    long long dp[100100]={0};
	    for(int i=1;i<=3;i++)
		{
	    	scanf("%d%lld",&w[i],&v[i]);
	    }
	    int L,R;
	    long long temp,A;
	    scanf("%d%d%lld%lld",&L,&R,&temp,&A);
	    for(int j=L;j<=R;j++)//从1~R优化成L~R,毕竟L、R很大
		{
            dp[j]=temp+(j-L)*A;
		}
	    for(int j=1;j<=T;j++)
		{
	        for(int i=1;i<=3;i++)
			{
	        	if(j>=w[i])
	          	dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	        }
            if(j>=R)
                dp[j]=max(dp[j],dp[j-R]+temp+(R-L)*A);
            if(j>=L)
                dp[j]=max(dp[j],dp[j-L]+temp);
	    }
	    printf("%lld\n",dp[T]);
	}
    return 0;
}

6.乘积最大

题目描述

今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:
设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
1) 3*12=36
2) 31*2=62
这时,符合题目要求的结果是:31*2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入描述:

第一行共有2个自然数N,K(6 ≤ N ≤ 40,1 ≤ K ≤ 6)
第二行是一个长度为N的数字串。

输出描述:

输出所求得的最大乘积(一个自然数)。
输入
4 2
1231
输出
62

链接:https://ac.nowcoder.com/acm/contest/1071/A

#include <iostream>
using namespace std;
int n,k;
string s;
long long dp[45][10];
long long num[45][45];
void fun(int x,int len)
{
	long long arr=0;
	string ss(s,x,len);
	for(int i=0;i<ss.length();i++)
	{
		arr=arr*10+(ss[i]-'0');
	}
	num[x+1][x+len]=arr;	
}
void solve()
{
	for(int i=1;i<=n;i++)
	dp[i][0]=num[1][i];
	for(int i=2;i<=n;i++)//第几个数 
	{
		for(int j=1;j<=k;j++)//第几刀 
		{
			for(int m=1;m<i;m++)//继承第几个关系数 
			{
				dp[i][j]=max(dp[m][j-1]*num[m+1][i],dp[i][j]);
			}
		}
	}
	cout<<dp[n][k]<<endl; 
}
int main()
{
	cin>>n>>k;
	cin>>s;
	for(int i=0;i<s.length();i++)
	{
		for(int j=1;j<s.length()-i+1;j++)
		fun(i,j);
	}
	solve();
	return 0;
} 

7.Make The Fence Great Again

You have a fence consisting of nn vertical boards. The width of each board is 11. The height of the ii-th board is aiai. You think that the fence is great if there is no pair of adjacent boards having the same height. More formally, the fence is great if and only if for all indices from 22 to nn, the condition ai−1≠aiai−1≠ai holds.

Unfortunately, it is possible that now your fence is not great. But you can change it! You can increase the length of the ii-th board by 11, but you have to pay bibi rubles for it. The length of each board can be increased any number of times (possibly, zero).

Calculate the minimum number of rubles you have to spend to make the fence great again!

You have to answer qq independent queries.

Input

The first line contains one integer qq (1≤q≤3⋅1051≤q≤3⋅105) — the number of queries.

The first line of each query contains one integers nn (1≤n≤3⋅1051≤n≤3⋅105) — the number of boards in the fence.

The following nn lines of each query contain the descriptions of the boards. The ii-th line contains two integers aiai and bibi (1≤ai,bi≤1091≤ai,bi≤109) — the length of the ii-th board and the price for increasing it by 11, respectively.

It is guaranteed that sum of all nn over all queries not exceed 3⋅1053⋅105.

It is guaranteed that answer to each query will not exceed 10181018.

Output

For each query print one integer — the minimum number of rubles you have to spend to make the fence great.

Example

input

3
3
2 4
2 1
3 5
3
2 3
2 10
2 6
4
1 7
3 3
2 6
1000000000 2

output

2
9
0

Note

In the first query you have to increase the length of second board by 22. So your total costs if 2⋅b2=22⋅b2=2.

In the second query you have to increase the length of first board by 11 and the length of third board by 11. So your total costs if 1⋅b1+1⋅b3=91⋅b1+1⋅b3=9.

In the third query the fence is great initially, so you don't need to spend rubles.

链接:https://codeforces.com/problemset/problem/1221/D

题意:相邻栅栏不能相同高度,每升1消耗b[i]价值,求修完n最小价值。

题解:第 i 栅栏,只考虑升0,1,2的高度。(因为对于每个独立栅栏,只受前后影响,和其他没有关系)

升0:前后都不干扰; 升1:前或者后干扰一个;升2: 前后都干扰。

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
#define INF 0x3f3f3f3f
long long read()
{
    char ch=getchar();
	long long x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}	
long long a[300005];
long long b[300005];
long long dp[300005][3];
void solve()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		b[i]=read();
	}
	dp[1][0]=0;
	dp[1][1]=b[1];
	dp[1][2]=b[1]+b[1];
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<3;j++)
		{
			dp[i][j]=1e18;//注意0x3f3f3f3f != 1e18
			for(int k=0;k<3;k++)
			{
				if(a[i]+j!=a[i-1]+k)
				dp[i][j]=min(dp[i][j],dp[i-1][k]+j*b[i]);
			}
		}
	}
	printf("%I64d\n",min(dp[n][0],min(dp[n][1],dp[n][2])));
}
int main()
{
	int q;
	scanf("%d",&q);
	while(q--)
	{
		solve();
	}
	return 0;
}
发布了39 篇原创文章 · 获赞 27 · 访问量 4143

猜你喜欢

转载自blog.csdn.net/qq_43381887/article/details/96452668