尺取法

一般是在给的一组数据中找到不大于某一个上限的“最优连续子序列”
给长度为n的数组和一个整数m,求总和不小于m的连续子序列的最小长度;

吃取法的基本思路是设置起始点与末尾点,然后末尾点不断自增直到满足一定条件,然后把该条件的一些情况记录,再把起始点自增,直到不满足条件,再自增末尾点,继续前面的,直到出界

1.延展尾点
2.退出条件
3.收缩前点

给出一个数组,求总和不小于m的连续子序列的最小长度

传送门
给出一个数组,求总和不小于m的连续子序列的最小长度

#include <iostream>
#include <cstdio>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e5+5;
int main(){
    int t,s,n;
    cin>>t;
    int a[N];
    while(t--){
        cin>>n>>s;
        for(int i=0;i<n;i++){scanf("%d",&a[i]);}
        int st=0,en=0;//下标,st表示起始位置,en表示末尾位置
        int ans=INF,sum=0;//ans表示答案,sum表示当前
        while(1){
            while(en<n&&sum<s)sum+=a[en++];
            if(sum<s)break;//因为前面while语句使得一直满足sum>=s,直到整个式子都没有这个情况了就退出
            ans=min(ans,en-st);//求取最短的子序列长度
            sum-=a[st++];
        }
        if(ans==INF)ans=0;
        printf("%d\n",ans);
    }
    return 0;
}

一本书有p页,每页有一个知识点,求最少页数覆盖所有知识点

传送门
同上面一题,不同的是加了set求知识点的总数以及map求每个知识点的个数
然后相同的方法即可

/*
一本书有p页,每页有一个知识点,求最少页数覆盖所有知识点
*/
#include <iostream>
#include <cstdio>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e6+5;
int a[maxn];
std::map<int, int> cnt;
set<int> t;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        t.insert(a[i]);
    }
    int num=t.size();//全部知识点
    int en=0,st=0;//st表示开始下标,en表示结束下标
    int sum=0;//目前学的知识点
    int ans=INF;//求最短的
    while(1){
        while(en<n&&sum<num){//扩展右边
            if(cnt[a[en++]]++==0)sum++;//没出现过的知识点进行记录,直到退出或者一段区间包含整个知识点
        }
        if(sum<num)break;//一般来说,从sum<num的循环中退出是不会执行这句,但从en<n退出表示都不满足条件了
        ans=min(ans,en-st);//此时[st,en]必定是满足条件的
        if(--cnt[a[st++]]==0)sum--;//缩短前面,如果在st后面还会出现的话,就缩短前面
    }
    printf("%d\n",ans);

    return 0;
}
/**
*set储存的是一串不重复的元素集合,且自动按照字典序进行排序
*insert()插入元素,size()获取元素个数,与map不同的是,map一对一地还存储了该元素的个数
*/

由连续素数相加为n的种类


埃氏素数筛+尺取法

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e4+5;
int prime[maxn];
int isprime[maxn];
int init(){
    for(int i=0;i<maxn;i++)isprime[i]=i;
    int k=0;
    isprime[0]=isprime[1]=0;
    for(int i=2;i<=maxn;i++){
        if(isprime[i]){
            prime[k++]=i;
            for(int j=2*i;j<=maxn;j+=i){
                isprime[j]=0;
            }
        }
    }
    return k;
}
int main(){
    int cnt=init();
    int n,k;
    while(~scanf("%d",&n)){
        if(n==0)break;
        int st=0,en=0;
        int sum=0;
        int ans=0;
        while(1){
            while(en<cnt&&sum<n)sum+=prime[en++];
            if(sum<n)break;
            if(sum==n)ans++;
            sum-=prime[st++];
        }
        printf("%d\n",ans);
    }
    return 0;
}

平分和为n的情况

传送门

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#define ll long long
using namespace std;
struct node{
    ll st,en;
};
vector<node> v;
int main(){
    ll n;
    while(~scanf("%lld",&n)){
        v.clear();
        ll st=1,en=1;
        ll sum=0;
        while(1){
            while(en*en<=n&&sum<n){sum+=(en*en);en++;}
            if(sum<n)break;
            if(sum==n)v.push_back((node){st,en-1});
            sum-=st*st;st++;
   
        }
        ll num=v.size();
        printf("%lld\n",num );
        for(int i=0;i<num;i++){
            printf("%lld",v[i].en-v[i].st+1);
            for(ll k=v[i].st;k<=v[i].en;k++)printf(" %lld",k);
            putchar('\n');
        }
    }
    return 0;
}


posted @ 2019-08-29 13:49  Emcikem  阅读(175)  评论(0编辑  收藏  举报