内存 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;
}