题目:
给定一个序列,从序列中选出几组连续不相交的区间,要求这几个区间中的数加起来一样大,问最多可以选出多少个区间。
思路:
比赛的时候看到这题,直接就想到 上去了… 但是实在没办法 ,然后自闭了…
很容易想到处理所有不同的区间和,然后对于每一个区间和再进行处理。因为最多只有 个不同的区间和,即最多 个不同的区间和,所以我们可以对于每个区间和进行处理。我也就是想到这里,然后转向了 …
正解是对于每个区间和,处理出这个区间和对应的多个区间,然后问题就变成了经典的选课问题。现在有 门课程,每门课程覆盖了一段时间,每个时间段只能上一门课,问最多可以选几门课。所以按照右端点排序,直接贪心即可。
反思:
和贪心有的时候会难以区分,导致错失正解。 的本质其实是搜索,是对暴搜的剪枝,去除了大量不正确的情况,通常来说列出了 方程之后, 正确性也就随之证明了。但是对于贪心来说,很多时候还是停留在试试的阶段,对贪心的掌握还是很差。
贪心通俗来说,就是一种在每次决策时采取当前意义下最优策略的算法,因此贪心要求问题的整体最优性可以由局部的最优性导出,但是贪心的正确性其实并不好证明。通常采取微扰法进行证明,即邻项交换。证明在任意局面下,任何对局面最优策略的微小改变都会造成整体结果变差。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 2000;
int a[N],sum[N],n,tot;
map<int,int> mp;
vector<pair<int,int> > v[N*N];
int solve(int x){
int tp = 0, last = 0;
sort(v[x].begin(),v[x].end());
rep(i,0,v[x].size()-1){
if(v[x][i].second > last) tp++, last = v[x][i].first;
}
return tp;
}
void output(int x)
{
if(x == -1) return;
int last = 0;
sort(v[x].begin(),v[x].end());
rep(i,0,v[x].size()-1){
if(v[x][i].second > last) {
last = v[x][i].first;
printf("%d %d\n",v[x][i].second,v[x][i].first);
}
}
}
int main()
{
mp.clear(); tot = 0;
scanf("%d",&n);
rep(i,1,n){
scanf("%d",&a[i]);
sum[i] = sum[i-1]+a[i];
}
rep(i,0,n-1)
rep(j,i+1,n){
int tp = sum[j]-sum[i];
if(mp.find(tp) == mp.end()) mp[tp] = ++tot;
int pos = mp[tp];
v[pos].push_back(make_pair(j,i+1));
}
int ans = 0, tph = -1;
rep(i,1,tot){
int tmp = solve(i);
if(tmp > ans){
ans = tmp, tph = i;
}
}
printf("%d\n",ans);
output(tph);
return 0;
}