新学的东西,用水题来练练手。
下面是套路:
int l = 1, r = 1;
while (退出条件)
{
while(不满足条件)
{
r++;
}
if (满足条件)
{
更新结果;
}
l++;
}
水题:
【1】http://poj.org/problem?id=3320
这道意思是一本书有n页,每一页上有一个知识点标号a[i]可能重复,要求选择一个最小的区间使得能够覆盖所有知识点.
用set来去重,用map来标记区间 [l, r)中各个知识点出现的次数(这里知识点的编号是有符号32位整数,而且是离散的,用bool形数组不方便处理,所以map是一个好选择),然后对 a[i] 进行尺取。
#include<iostream>
#include<cstdio>
#include<map>
#include<set>
using namespace std;
map<int, int> mp;
set<int> num;
int a[1000010];
int main()
{
int n;
scanf("%d",&n);
for (int i=0; i<n; i++)
{
scanf("%d",&a[i]);
num.insert(a[i]);
}
int m = num.size();
int l = 0, r = 0;
int cnt = 0;
int ans = (1<<31)-1;
while (l <= r)
{
while (cnt < m && r < n)
{
mp[a[r]]++;
if (mp[a[r]] == 1)
cnt++;
r++;
}
if (cnt < m) break;
ans = min(ans,r-l);
mp[a[l]]--;
if (mp[a[l]] <= 0)
cnt--;
l++;
}
cout<<ans<<endl;
return 0;
}
【2】 http://poj.org/problem?id=2566
给你一个数组(元素可为负数)m次询问,每次询问一个区间使得区间和的绝对值最接近给定的值,有多种随意输出一种。
预处理前缀和sum[i]这样可以O(1)查询区间和,然后sum数组从小到大排序(因为abs(sum[i]-sum[j])=abs(sum[j]-sum[i])所以顺序不影响答案,但可以方便尺取)然后就是O(n)推进,每次有最小值是更新答案区间信息。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<utility>
#include<algorithm>
using namespace std;
pair<int, int> p[1000010];
int main()
{
int n, k;
while (scanf("%d%d",&n,&k) && n && k)
{
int s = 0;
p[0] = pair<int,int>(0,0);
for (int i=1; i<=n; i++)
{
int x;
scanf("%d",&x);
s += x;
p[i] = pair<int,int>(s,i);
}
sort(p,p+n+1);
for (int i=0; i<k; i++)
{
int t;
scanf("%d",&t);
int l = 0, r = 1;
int ans_l = 0, ans_r = 1,ans_t = -1;
int dis = (1<<31)-1;
while (l<=n && r<=n)
{
int y = p[r].first - p[l].first;
if (abs(y-t) < dis)
{
ans_l = p[l].second;
ans_r = p[r].second;
ans_t = y;
dis = abs(y-t);
}
if (y > t)
l++;
else if (y < t)
r++;
else break;
if (l == r) r++;
}
if (ans_r < ans_l) swap(ans_r, ans_l);
cout<<ans_t<<' '<<ans_l+1<<' '<<ans_r<<endl;
}
}
return 0;
}
【3】
http://poj.org/problem?id=2739
给你一个数,询问有多少个连续质数序列和等于该数例如53=5 + 7 + 11 + 13 + 17 。
预处理出一个素数表,计算前缀和,对前缀和进行尺取。
#include<cstdio>
#include<iostream>
using namespace std;
bool del[10000];
int p[10000];
int s[10000];
void init()
{
for (int i=2; i<10000; i++)
if (!del[i])
{
p[++p[0]] = i;
int j = i*2;
while (j < 10000)
{
del[j] = true;
j += i;
}
}
for (int i=1; i<=p[0]; i++)
s[i] = s[i-1] + p[i];
}
int calc(int n)
{
int l = 1, r = 1;
int cnt = 0;
int x = -1;
while (l<=n && r<=n)
{
while (s[r]-s[l-1] < n && r <= n)
{
r++;
}
if (s[r] - s[l-1] == n)
cnt++;
l++;
}
return cnt;
}
int main()
{
init();
int n;
while (scanf("%d",&n) && n)
{
printf("%d\n",calc(n));
}
return 0;
}
【4】 http://poj.org/problem?id=2100
给你一个数,询问有多少种连续自然数的平方和等于这个数,输出所有可能。
和上一个题一样,只不过退出条件不同,提交错了好多次>_<!
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef unsigned long long ull;
struct node
{
ull l, r;
node(ull l, ull r):l(l), r(r) {}
};
vector<node> ans;
int cmp(const node& a, const node& b)
{
return a.r-a.l+1 > b.r-b.l+1;
}
void solve(ull n)
{
ull l = 1, r = 1;
ull sum = 0;
while (true)
{
while (sum < n)
{
sum += r*r;
r++;
}
if ((r-1)*(r-1) > n) break;
if (sum == n)
{
ans.push_back(node(l,r));
}
sum -= l*l;
l++;
}
printf("%d\n",ans.size());
for (ull i=0; i<ans.size(); i++)
{
node now = ans[i];
printf("%lld",now.r-now.l);
for (ull j=now.l; j<now.r; j++)
printf(" %lld",j);
printf("\n");
}
}
int main()
{
ull n;
while (scanf("%lld",&n)!=EOF && n)
solve(n);
return 0;
}