[noip模拟赛2017.7.12]

内存 256M 时限 1s

最大和(sum)

题目描述

N 个数围成一圈,要求从中选择若干个连续的数(注意每个数最多只能选一次)加起来, 问能形成的最大的和。

输入格式:

第一行输入 N,表示数字的个数,第二行输入这 N 个数字。

输出格式:

输出最大和。

样例输入:

8
2 -4 6 -1 -4 8 -1 3

样例输出:

14

数据说明:

40% 1<=N<=300 60% 1<=N<=2000 100% 1<=N<=100000,答案在 longint 范围内。

题解

对于链状最大和,显然可以贪心O(n)完成,而环状最大和,只可能在链上或者在两端,如果在两端,显然,中间一段一定是最小子段和,那么我们用总和减去最小链状子段和,即可求得环状的最大子段和

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long  
using namespace std;
const int Num_Ary=100000;
int n,a[Num_Ary];
int mx[Num_Ary],mi[Num_Ary];
int ans,sum;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),sum+=a[i];
	for(int i=1;i<=n;i++){
		mx[i]=max(mx[i-1]+a[i],max(a[i],0));
		mi[i]=min(mi[i-1]+a[i],min(a[i],0));
		ans=max(ans,mx[i]);ans=max(ans,sum-mi[i]);
	}
	printf("%d",ans);	
}

修剪花卉

问题背景:

ZZ对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。 一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花 卉的问题。 于是当日课后,ZZ就向老师提出了这个问题: 一株奇怪的花卉,上面共连有N 朵花,共有N-1条枝干将花儿连在一起,并且未修剪时每 朵花都不是孤立的。 每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的, 说明这朵花看着都让人恶心。 所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。 经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。 老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那 朵)花卉上所有花朵的“美丽指数”之和最大。 老师想了一会儿,给出了正解(交大的老师是很牛的~)。ZZ见问题被轻易攻破,相当不爽, 于是又拿来问你。

输入说明:

第一行一个整数N(1 ≤ N ≤ 16000)。表示原始的那株花卉上共N 朵花。 第二行有N 个整数,第I个整数表示第I朵花的美丽指数。 接下来N-1行每行两个整数a,b,表示存在一条连接第a 朵花和第b朵花的枝条。 ## 输出说明:
一个数,表示一系列“修剪”之后所能得到的“美丽指数”之和的最大值。保证绝对值不超 过2147483647。

样例输入:

7

-1 -1 -1 1 1 1 0

1 4

2 5

3 6

4 7

5 7

6 7

样例输出:

3

数据范围:

对于 60%的数据, 保证N≤1,000 对于 100%的数据,保证 N≤16,000

题解

此题略水,直接树型DP,每次返回子数最优值,用来更新ans即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long  
using namespace std;
int n,ans;
int a[40000],b[40000],nt[40000],p[40000],v[40000],cnt=1;
void add(int x,int y){
	a[cnt]=x;b[cnt]=y;
	nt[cnt]=p[x];p[x]=cnt++;
}
bool vis[40000];
int dfs(int k){
	int rtn=0;vis[k]=true;
	for(int i=p[k];i;i=nt[i]){
		int kk=b[i];
		if(!vis[kk])
			rtn=max(rtn,rtn+dfs(kk));
	}
	ans=max(ans,rtn+v[k]);
	return rtn+v[k];
}
int main(){
	freopen("makeup.in","r",stdin);
	freopen("makeup.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&v[i]);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	dfs(1);
	printf("%d",ans);
	return 0;
}

金字塔(pyramid)

题目描述:

小 X 来到一个雄奇的金字塔挖宝,但是这是一座被诅咒的金字塔,小 X 必须马上逃离这里, 否则小 X 就会被埋在金字塔里,但他不希望此行落空。 现在小 X 面前有 N+1 种财宝,每种财宝都有一个价值。第一种财宝重量为 0,第二种财宝 重量为 1,总之第 I 种财宝重量为 I-1。现在小 X 希望拿走 N+M 个物品,但是这 M+N 个物品 总重量不能超过 N。小 X 希望能获得最大的价值。你能帮帮他吗? 由于金字塔跟小 X 一样牛,所以每种财宝无限个。

输入格式:

第一行两个正整数 N,M 第二行 N+1 个整数,第 I 个整数代表了第 I 种财宝的价值

输出格式:

一个数,表示最大利润。

样例输入:

5 3

4 7 2 5 -3 6

样例输出:

47

数据范围:

10%满足 N,M<=10 40%满足 N,M<=100 100%满足 N,M<=3000abs(财宝价值)<=1000

题解

杂技DP,我们考虑重量为0的物品,它的存在不受重量的限制,可作为本题的突破口,因为任何一种状态,要想满足题设要求,必须要重量为0的那个物品填充数量,我们记重量为i这个状态所拿的物品数量(不计重量为0的)为t[i],价值为f[i],容易得到,最终价值w[i]=f[i]+(m+n-t[i])*v[0],转移方程时,选取使w[i]最大的方案进行转移即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long  
#define INF (1<<30)
using namespace std;
int n,m,v[10010];
int f[10010],t[10010],ans=-INF;
int main(){
	freopen("pyramid.in","r",stdin);
	freopen("pyramid.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)f[i]=-INF;
	for(int i=0;i<=n;i++)
		scanf("%d",&v[i]);
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			if(f[j]+(m+n-t[j])*v[0]<f[j-i]+v[i]+(m+n-t[j-i]-1)*v[0]){
				t[j]=t[j-i]+1;
				f[j]=f[j-i]+v[i];
			}
		}
	}
	for(int i=0;i<=n;i++)
		ans=max(ans,(m+n-t[i])*v[0]+f[i]);
	printf("%d",ans);
	return 0;
}

posted @ 2017-07-15 18:51  Anoxiacxy  阅读(407)  评论(0编辑  收藏  举报