法一:贪心:
维护一个sum与一个sumMax,如果当前sum < 0,则将sum置0,并修改起点为当前的下一位,如果当前sum > sumMax,则修改终点与sumMax
#include <iostream>
using namespace std;
int main(void) {
int T;
cin >> T;
for(int i = 1; i <= T; i++) {
int maxSum = INT_MIN;
int N;
cin >> N;
int sum = 0;
int startMax = 1, endMax = 1;
int start = 1;
for(int j = 1; j <= N; j++) {
int a = 0;
cin >> a;
sum += a;
if(sum > maxSum) {
maxSum = sum;
startMax = start;
endMax = j;
}
if(sum < 0) {
sum = 0;
start = j + 1;
}
}
cout << "Case " << i << ":" << endl;
cout << maxSum << ' ' << startMax << ' ' << endMax << endl;
if(i != T) cout << endl; //输出还得真讲究
}
return 0;
}
第一次写错误
(1)误以为start会与j绑定,j递增start也会递增,实际上根本不会,不晓得怎么脑子就在这么简单地方宕机了
(2)严格遵循输出要求,最后一行不能输出换行符
(3)最开始sumMax要设为INT_MIN,不能是0,否则对于全是负数会出错
法二:动态规划
(一)主要思路:
(1)dp[ i ]:以a[ i ]结尾的最大子序和,注意必须包含结尾a[ i ]
(2)递推公式:因为dp[ i ]是由dp[ i - 1 ]推出来的,所以再有dp[ i ]定义,dp[ i ] = max(dp[ i - 1 ] + a[ i ], a[ i ])
(3)遍历顺序:从前往后
(4)初始化:dp[ 1 ] = a[ 1 ],其他初始化为0
(二)代码
#include <iostream>
#include <vector>
using namespace std;
int main(void) {
int T;
cin >> T;
for(int i = 1; i <= T; i++) {
int num;
cin >> num;
vector<int> a(num + 1, 0);
for(int j = 1; j <= num; j++) {
cin >> a[j];
}
vector<int> dp(num + 1, 0); //dp[i]:以a[i]结尾的最大子序和 ,即无论a[i]正负必须包括它
dp[1] = a[1]; //初始化:dp[1]初始化为 a[1],其余初始化为0
int start = 1, end = 1, p = 1;
int sumMax = dp[1];
for(int j = 2; j <= num; j++) {
// dp[j] = max(a[j], dp[j - 1] + a[j]); 递推公式,但因为要记录起点与众点所以要另外判断
if(dp[j - 1] < 0) {
dp[j] = a[j];
p = j;
}
else {
dp[j] = dp[j - 1] + a[j];
}
if(dp[j] > sumMax) {
sumMax = dp[j];
start = p;
end = j;
}
}
// for(int j = 1; j <= num; j++) cout << dp[j] << ' ';
// cout << endl;
// cout << "=========================" << endl;
cout << "Case " << i << ":" << endl;
cout << sumMax << ' ' << start << ' ' << end << endl;
if(i != T) cout << endl;
}
return 0;
}
(三)第一次写错误:
(1)忘记为start设置中间变量p了
补充:
(1)不论是贪心还是动态规划都有个共同点,就是重置区间开头与结尾,都要三用个变量start,end,p,如果子序和出现负数,则要将区间开头重置,重置的开头存储在p里,如果搜素到更大区间子序和,则要同时重置区间开头,结尾,最大子序和,开头就是将p赋给start,结尾就是当前遍历位置(细细想来贪心也是维护到以当前遍历的位置为结尾的子序和哎)
(2)动态规划其实可以省略a[ i ]直接就用一个数组dp读取,代码如下
// hdu 1003的DP代码
#include<bits/stdc++.h>
using namespace std;
int dp[100005]; //dp[i]: 以第i个数为结尾的最大值
int main(){
int t; cin>>t;
for(int k=1;k<=t;k++){
int n; cin >> n;
for(int i=1;i<=n;i++) cin >> dp[i]; //就用dp[]存数据a[]
int start=1, end=1, p=1; //起点,终点,扫描位置
int maxsum = dp[1];
for(int i=2; i<=n; i++){
if(dp[i-1]+dp[i] >= dp[i]) //转移方程dp[i]=max(dp[i-1]+a[i], a[i]);
dp[i] = dp[i-1]+dp[i]; // dp[i-1]+a[i]比a[i]大
else p = i; // a[i] 更大,那么dp[i]就是a[i]
if(dp[i]> maxsum ) { //dp[i]是一个更大的子序和
maxsum = dp[i]; start = p; end = i; //以p为开始, 以i为结尾
}
}
printf("Case %d:\n",k); printf("%d %d %d\n", maxsum,start,end);
if(k != t) cout << endl;
}
}