ACM暑期集训13

今天听艾神的课,许多题目都涉及区间dp,于是自己在网上找了个区间dp小结 传送门 ,按照这里的题目从上开始刷,下面写的主要是做题的题解。

石子合并(一)

时间限制:1000 ms  |  内存限制:65535 KB

描述

    有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。

难度:3

输入

有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开

输出

输出总代价的最小值,占单独的一行

样例输入

3
1 2 3
7
13 7 8 16 21 4 18
 

样例输出

9

239

 分析:要合并区间[i,j]的石子,使花费最少,则要从i到j中选某一个位置k,合并[i,k]和[k+1,j],枚举k,取其中的最小值

用dp[i][j]来表示合并第i堆到第j堆石子的最小花费。

状态转移方程 :dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);

#include
#include
#include
#include
#include
using namespace std;

int a[210],sum[210];
int dp[210][210];

int main()
{
	int n;
	while(~scanf("%d",&n))
	{
		sum[0]=0;
		memset(dp,0x3f,sizeof(dp));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			dp[i][i]=0;
			sum[i]=sum[i-1]+a[i];
		}

		for(int len=2;len<=n;len++)
			for(int i=1;i<=n;i++)
		{
			int j=i+len-1;
				if(j>n) continue;
			for(int k=i;k

采用平行四边形优化 

根据前面的一些最有k的位置,后面的一些区间k,的位置不用从前往后找

用s[i][j]表示区间[i,j]中的最优分割点

我们可以证明,s[i,j-1]≤s[i,j]≤s[i+1,j]  证明见 传送门

 for(int k=s[i][j-1];k<=s[i+1][j];k++)
            {
                if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]

Monkey Party

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2431    Accepted Submission(s): 984

 

Problem Description

Far away from our world, there is a banana forest. And many lovely monkeys live there. One day, SDH(Song Da Hou), who is the king of banana forest, decides to hold a big party to celebrate Crazy Bananas Day. But the little monkeys don't know each other, so as the king, SDH must do something. 
Now there are n monkeys sitting in a circle, and each monkey has a making friends time. Also, each monkey has two neighbor. SDH wants to introduce them to each other, and the rules are: 
1.every time, he can only introduce one monkey and one of this monkey's neighbor. 
2.if he introduce A and B, then every monkey A already knows will know every monkey B already knows, and the total time for this introducing is the sum of the making friends time of all the monkeys A and B already knows; 
3.each little monkey knows himself; 
In order to begin the party and eat bananas as soon as possible, SDH want to know the mininal time he needs on introducing. 

Input

There is several test cases. In each case, the first line is n(1 ≤ n ≤ 1000), which is the number of monkeys. The next line contains n positive integers(less than 1000), means the making friends time(in order, the first one and the last one are neighbors). The input is end of file.

Output

For each case, you should print a line giving the mininal time SDH needs on introducing.

Sample Input

8 5 2 4 7 6 1 3 9

Sample Output

105

分析:

我们可以把前n-1堆石子一个个移到第n个后面,那样环就变成了线,问题就变的和上面非常相似,只是结果是

dp[i][n-i+1](1<=i<=n)最小值

#include
#include
#include
#include
#include
using namespace std;

int a[2010],sum[2010];
int dp[2010][2010];
int s[2010][2010];

int main()
{
	int n;
	while(~scanf("%d",&n))
	{
		sum[0]=0;
		memset(dp,0x3f,sizeof(dp));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			dp[i][i]=0;
			s[i][i]=i;
			sum[i]=sum[i-1]+a[i];
		}
		for(int i=1;i2*n-1) break;
			for(int k=s[i][j-1];k<=s[i+1][j];k++)
			{
				if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]){
                     dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                     s[i][j]=k;
                 }
			}

		}
		int ans=0x3f3f3f3f;
		for(int i=1;i<=n;i++)
			ans=min(ans,dp[i][i+n-1]);

		printf("%d\n",ans);
	}
	return 0;
}

Brackets

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 11894   Accepted: 6289

Description

We give the following inductive definition of a “regular brackets” sequence:

  • the empty sequence is a regular brackets sequence,
  • if s is a regular brackets sequence, then (s) and [s] are regular brackets sequences, and
  • if a and b are regular brackets sequences, then ab is a regular brackets sequence.
  • no other sequence is a regular brackets sequence

For instance, all of the following character sequences are regular brackets sequences:

(), [], (()), ()[], ()[()]

while the following character sequences are not:

(, ], )(, ([)], ([(]

Given a brackets sequence of characters a1a2 … an, your goal is to find the length of the longest regular brackets sequence that is a subsequence of s. That is, you wish to find the largest m such that for indices i1, i2, …, im where 1 ≤ i1 < i2 < … < im ≤ nai1ai2 … aim is a regular brackets sequence.

Given the initial sequence ([([]])], the longest regular brackets subsequence is [([])].

Input

The input test file will contain multiple test cases. Each input test case consists of a single line containing only the characters ()[, and ]; each input test will have length between 1 and 100, inclusive. The end-of-file is marked by a line containing the word “end” and should not be processed.

Output

For each input case, the program should print the length of the longest possible regular brackets subsequence on a single line.

Sample Input

((()))
()()()
([]])
)[)(
([][][)
end

Sample Output

6
6
4
0
6

分析:

用dp[i][j]表示区间[i,j]里最大完全匹配数。

 

只要得到了dp[i][j],那么就可以得到dp[i-1][j+1]

dp[i-1][j+1]=dp[i][j]+(s[i-1]与[j+1]匹配 ? 2 : 0)

 

然后利用状态转移方程更新一下区间最优解即可。

dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j])

 

 

#include
#include
#include
#include
#include
using namespace std;

char s[110];
int dp[110][110];


int main()
{

	while(~scanf("%s",s+1))
	{
		if(s[1]=='e') break;
		int n=strlen(s+1);
		memset(dp,0,sizeof(dp));
		for(int len=2;len<=n;len++)
			for(int i=1;i<=n;i++)
		{
			int j=i+len-1;
			if(j>n) break;
			if( ( s[i]=='('&&s[j]==')' ) || (s[i]=='['&&s[j]==']') )
				dp[i][j]=dp[i+1][j-1]+2;
			for(int k=i;k

 

括号匹配(二)

时间限制:1000 ms  |  内存限制:65535 KB

难度:6

描述

给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的

输入

第一行输入一个正整数N,表示测试数据组数(N<=10)
每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100

输出

对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行

样例输入

4
[]
([])[]
((]
([)]

样例输出

0
0
3
2

分析:
如果匹配的越多,那么需要添加的就可以越少

则所求=原序列括号数量-最大匹配括号数量 

#include
#include
#include
#include
#include
using namespace std;

char s[110];
int dp[110][110];


int main()
{
    int n;
    scanf("%d",&n);
	while(n--)
	{
		scanf("%s",s+1);
		int n=strlen(s+1);
		memset(dp,0,sizeof(dp));
		for(int len=2;len<=n;len++)
			for(int i=1;i<=n;i++)
		{
			int j=i+len-1;
			if(j>n) break;
			if( ( s[i]=='('&&s[j]==')' ) || (s[i]=='['&&s[j]==']') )
				dp[i][j]=dp[i+1][j-1]+2;
			for(int k=i;k

整数划分(四)

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述

       暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近遇到了一个难题,让他百思不得其解,他非常郁闷。。亲爱的你能帮帮他吗?

      问题是我们经常见到的整数划分,给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积

输入

第一行是一个整数T,表示有T组测试数据
接下来T行,每行有两个正整数 n,m ( 1<= n < 10^19, 0 < m <= n的位数);

输出

输出每组测试样例结果为一个整数占一行

样例输入

2
111 2
1111 2

样例输出

11
121

分析:

用dp[i][j]表示从第一位到第i位共插入j个乘号后乘积的最大值。怎样进行状态转移呢?我们可以从插入较少乘号的结果算出插入较多乘号的结果。方法是当我们要放第j的乘号时枚举放的位置。

状态转移方程为:

dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i])

其中num[i][j]表示从s[i]到s[j]这段连续区间代表的数值。

#include
#include
#include
#include
#include
using namespace std;

typedef long long ll;
char s[30];
ll dp[30][30];
ll num[30][30];

int main()
{
    int t,m;
    scanf("%d",&t);
	while(t--)
	{
		scanf("%s%d",s+1,&m);
		int n=strlen(s+1);
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n;i++)
		{
			num[i][i]=s[i]-'0';
		    for(int j=i+1;j<=n;j++)
				num[i][j]=num[i][j-1]*10+s[j]-'0';
		}
		for(int i=1;i<=n;i++)
			dp[i][0]=num[1][i];
		for(int j=1;j

 

4. 凸多边形三角划分问题

Problem Description

给定一个具有N(N<=50)个顶点(从1到N编号)的凸多边形,每个顶点的权值已知。问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权值的乘积之和最小。

Input

第一行为顶点数N,第二行为N个顶点(从1到N)的权值。

Output

乘积之和的最小值。题目保证结果在int范围内。

Sample Input

5

1 6 4 2 1

5

121 122 123 245 231

Sample Output

34

12214884

分析:

用dp[i,j]表示从顶点i到顶点j的凸多边形三角剖分后所得到的最大乘积。枚举i到j中的点作为分割点

可知状态转移方程:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]); 

#include
#include
#include
#include
#include
using namespace std;

typedef long long ll;
int a[55];
ll dp[55][55];
ll num[55][55];

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
    	memset(dp,0x3f3f,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++) dp[i][i+1]=0;
        for(int len=3;len<=n;len++)
        for(int i=1;i<=n;i++)
        {
            int j=i+len-1;
            if(j>n) break;
            for(int k=i+1;k

 

 

 

 

 

 

 

你可能感兴趣的:(【18暑期集训】)