程序设计思维与实践 Week12 作业

C - 必做题 - 3

题目:
东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人
这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j)
问题是要找到sum(i1, j1) + … + sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。
注:1 ≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)
Input
输入m,输入n。后面跟着输入n个ai 处理到 EOF
Output
输出最大和

思路:
该题是一题较难的动态规划题。我认为该题主要难在状态的生成及状态如何转移上。这里我采用了状态f[i][j]表示前j个宿舍扫i次楼的最大和(必包含aj)。状态转移方程为:f[i][j]=max(f[i][j-1]+a[j],f[i-1][k]+a[j]),其中i-1=<k<j,即aj是否分开一次扫。该题中,由于数据量过大,无法直接采用二维数组。但可以看到,计算f[i][j]只需要知道f[i][j-1]和f[i-1]即可。因此,这里我们将二维数组简化为两个一维数组。一个用于存放f[i],一个存放f[i-1]。之后只要按照状态转移方程对数组跑dp即可,跑的过程中要注意两个一维数组的迭代。

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
int a[1000050];
int f[1000050];
int dp[1000050];
void init()
{
	for(int i=0;i<1000050;i++)
	{
		f[i]=0;
		dp[i]=0;
	}
}
int main()
{
	int m,n;
	while(scanf("%d %d",&m,&n)!=EOF)
	{
		init();
		int ans=-10000000;
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		dp[0]=0;
		for(int i=1;i<=m;i++)
		{
			ans=-10000000;
			for(int j=i;j<=n;j++)
			{
				dp[j]=max(dp[j-1]+a[j],f[j-1]+a[j]);
				f[j-1]=ans;
				if(dp[j]>ans)
					ans=dp[j];
			}
		}
		printf("%d\n",ans);
	}
}

D - 选做题 - 1

题目
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 ≤ n, ai1ai2 … 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.

思路
这题应该使用区间dp 来解决。考虑到是子序列,有不连续性,于是根据其形态不同转移方程如下:设 f[ x ][ y ] 是括号序列第x个到第y个的最长正则括号子序列的长度,s为括号序列,若 s[ x ]与 s[ y ]为一对相互匹配的括号, 则f[x][y] = max(f[x+1][y-1], max(f[x][k]+f[k+1][y] ) ) (y-1>=k>=x)
否则,
f[x][y] = max(f[x+1][y], f[x][y-1] , max(f[x][k]+f[k+1][y] ) ) (y-1>=k>=x)
最后输出 f[1][s.size()] 即可。

代码

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int f[200][200];
void init()
{
	for(int i=0;i<200;i++)
	{
		for(int j=0;j<200;j++)
		{
			f[i][j]=0;
		}
	}
}
int main()
{
	string s;
	while(cin>>s&&s!="end")
	{
		for(int i=1;i<=s.size()-1;i++)
		{
			for(int j=1;j+i<=s.size();j++)
			{
				if((s[j-1]=='('&&s[i+j-1]==')')||(s[j-1]=='['&&s[i+j-1]==']'))
				{
					int max=f[j+1][i+j-1]+2;
					for(int k=j+1;k<=i+j-2;k++)
					{
						if(f[j][k]+f[k+1][i+j]>max)
							max=f[j][k]+f[k+1][j+i];
					}
					f[j][i+j]=max;
				}
				else
					{
						int ma=max(f[j+1][i+j],f[j][i+j-1]);;
						for(int k=j+1;k<=i+j-2;k++)
						{
							if(f[j][k]+f[k+1][i+j]>ma)
								ma=f[j][k]+f[k+1][j+i];
						}
						f[j][i+j]=ma;
					}
			}
		}
		cout<<f[1][s.size()]<<endl;
		s.clear();
	}
}

E - 选做题 - 2

题目
Description
马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
Input
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数 n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100) S 表示任务的名称和两个整数 D 和 C,分别表示任务的截止时间和完成任务需要的天数。
这 n 个任务是按照字符串的字典序从小到大给出。
Output
每组测试数据,输出最少扣的分数,并输出完成作业的方案,如果有多个方案,输出字典序最小的一个。

思路
这题因为数据范围较小,因此可以采用状压dp,设整数S为当前完成的作业的合集,f[S] 完成作业合集S被扣的最少分数,sum为当前作业合集所花的总时间,x为作业,若x未在合集中且从未计算过f[S|(1<<x)],有转移方程:
f[S|(1<<x)] = f[S] + max ( sum + c[x] – d[x], 0 )
若计算过f[S|(1<<x)],有转移方程:
f[S|(1<<x)] = max (f[S] + max ( sum + c[x] – d[x], 0 ) , f[S|(1<<x)] )
在计算转移方程时同时用pre数组记录下当前作业合集的前一个状态,用于最后递归输出作业顺序。同时因为题目要求多种情况输出字典序小的情况,因此需要按照字典序由小到大的顺序对状态进行遍历,这样就能保证输出的字典序最小。同时f[2^n-1]为最少扣的分数。

代码

#include <iostream>
#include <string>
#include <math.h>
#include <algorithm>
using namespace std;
int f[32800];
string str[20];
int d[20];
int c[20];
int pre[32800];
int pre2[32800];
int sum[32800];
void init()
{
	for(int i=0;i<32800;i++)
	{
		f[i]=-1;
		pre[i]=-1;
		sum[i]=0;
		pre2[i]=-1;
	}
	f[0]=0;
	for(int i=0;i<20;i++)
	{
		d[i]=0;c[i]=0;
		str[i].clear();
	}
}
void output(int s)
{
	if(pre[s]==0)
	{
		int t=log(s)/log(2);
		cout<<str[t]<<endl;
		return;
	}
	output(pre[s]);
	int tmp=log(s-pre[s])/log(2);
	cout<<str[tmp]<<endl;
}
int main()
{
	int m;
	cin>>m;
	for(int v=0;v<m;v++)
	{
		init();
		int n;
		cin>>n;
		for(int i=0;i<n;i++)
		{
			cin>>str[i];
			cin>>d[i];
			cin>>c[i];
		}
		for(int i=0;i<pow(2,n);i++)
		{
			for(int j=0;j<n;j++)
			{
				if((i&1<<j)==0)
				{
					if(f[i|1<<j]==-1)
					{
						f[i|1<<j]=f[i]+max(sum[i]+c[j]-d[j],0);
						sum[i|1<<j]=sum[i]+c[j];
						pre[i|1<<j]=i;
					}
					else
					{
						if(f[i|1<<j]>f[i]+max(sum[i]+c[j]-d[j],0))
						{
							f[i|1<<j]=f[i]+max(sum[i]+c[j]-d[j],0);
							sum[i|1<<j]=sum[i]+c[j];
							pre[i|1<<j]=i;
						}
					}
				}
			}
		}
		int t=pow(2,n)-1;
		cout<<f[t]<<endl;
		output(pow(2,n)-1);
	}
} 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值