尺取法预习题

Powered by:AB_IN 局外人

P1147 连续自然数和

旧题新做,老师说用等差数列做,菜鸡正在研究。。
刚学了尺取法,即双指针。
学姐说双指针面试很注重。。
看来得好好学。
先拿水题练练手。

#include <bits/stdc++.h>
using namespace std;
int a[2000010],n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        a[i]=i;
    int i=1,j=1;
    int sum=a[1];
    while(i<=j&&j<n){
        if(sum>=n){
            if(sum==n) printf("%d %d\n",i,j);
            sum-=a[i];//左指针先减数
            i++;
        }
        else{
            j++;//右指针先自增
            sum+=a[j];
        }
    }
    return 0;
}

奥林oj跑18ms,作弊 +改输入输出跑7ms。O(n)算法果然名不虚传。。
前面有这个题前缀和+二分的做法。(跑的比较慢)

poj3061 Subsequence

题意:给定一个序列,使得其和大于或等于s,求最短的子序列长度。

#include <bits/stdc++.h>
using namespace std;
int a[2000010],n,t,s;
int main()
{
    scanf("%d",&t);
    while(t--){
       scanf("%d%d",&n,&s);
       int ans=0x3f3f3f3f;
       memset(a,0,sizeof(a));
       for(int i=1;i<=n;i++)
           scanf("%d",&a[i]);
       int i=1,j=1;
       int sum=a[1];
       while(i<=j&&j<=n){
           if(sum>=s){
               ans=min(ans,j-i+1);
               sum-=a[i];
               i++;
           }
           else{
               j++;
               sum+=a[j];
           }
       }
       if(ans==0x3f3f3f3f) ans=0;
       printf("%d\n",ans);
    }
    return 0;
}

poj2739 Sum of Consecutive Prime Numbers

题意:问一个数拆成几个连续素数的和共有多少种方案。

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int l,r,n,len,cnt,sum,ans,prime[N],pre[N];
bool flag[N];
void init()
{
    memset(flag,1,sizeof(flag));
    flag[1]=cnt=0;
    for(int i=2;i<=N;i++)
    {
        if(flag[i])
        {
            prime[++cnt]=i;
            pre[i]=cnt;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=N;j++)
        {
            flag[prime[j]*i]=0;
            if(i%prime[j]==0)break;
        }
    }
}
int main()
{
    init();
    while(cin>>n&&n){
        len=lower_bound(prime+1,prime+n+1,n)-prime;
        int i=1,j=1,ans=0;
        int sum=prime[1];
        while(i<=j&&j<=len){
            if(sum>=n){
               if(sum==n) ans++;
               sum-=prime[i];
               i++;
            }
            else{
               j++;
               sum+=prime[j];
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

这里更一下素数筛。(代码来自学长)以后就直接套模版了[doge]。

const int N=1e4+5;
int  cnt,sum,ans,prime[N],pre[N];
bool flag[N];
void init()
{
    memset(flag,1,sizeof(flag));
    flag[1]=cnt=0;
    for(int i=2;i<=N;i++)
    {
        if(flag[i])
        {
            prime[++cnt]=i;
            pre[i]=cnt;
        }
        for(int j=1;j<=cnt&&prime[j]*i<=N;j++)
        {
            flag[prime[j]*i]=0;
            if(i%prime[j]==0)break;
        }
    }
}

pre[i]的妙用:
1.若pre[i]!=0,则它为素数。
2.pre[i]为 i是第几个素数。

prime[i] 便为一个素数数组。

牛客网字符串

题意:给定一个只含小写字母的字符串,使得其包含26个小写字母,求最短的子串长度。
问:怎么实时判断字母种类有多少了?
答:用一个桶排,字母当下标,当桶排数为1时 cnt++。当桶排数为0时 cnt--

#include <bits/stdc++.h>
using namespace std;
int vis[130];//ASCII码总共127个
string a;
int main()
{
    ios::sync_with_stdio(false);
    cin>>a;
    int n=a.size();
    int i=0,j=0;
    int ans=0x3f3f3f3f;
    int sum=1;vis[a[0]]++;//菜鸡的尺取写法,必须先处理第一个元素
    while(i<=j&&j<n){
        if(sum>=26){
            ans=min(ans,j-i+1);
            vis[a[i]]--;
            if(vis[a[i]]==0) sum--;//sum就表示26个字母只含一次的数量
            i++;
        }
        else{
            j++;
            vis[a[j]]++;
            if(vis[a[j]]==1) sum++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

将字符作为下标的数组,不是把字符转换为ASCII码作为下标,而是根据ASCII码的顺序作为下标存入。
比如:

char a='g';
vis[a]++;

在这里插入图片描述
观察到从下标1开始。
继续

char b='j';
vis[b]++;

在这里插入图片描述
会空上h和i的空。
继续

char c='a';
vis[c]++;

在这里插入图片描述
a作为比g和j更前的,就取代了1的位置。
这样一来,就可以不用用map<char,int>vis了,直接用vis[130]即可。
这个题菜鸡用map跑508ms,用数组只跑了25ms.

poj2100 Graveyard Design

题意:找到某一个区间使得区间内的数的平方和等于某一值m。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct sa
{
    ll l;
    ll r;
    ll ans;
}a[10001];
ll m,n;
int main()
{
    cin>>m;
    ll n=sqrt(m);
    ll i=1,j=1;
    ll sum=1,cnt=0;
    while(i<=j&&j<=n){
        if(sum>=m){
            if(sum==m){
                cnt++;
                a[cnt].l=i;
                a[cnt].r=j;
                a[cnt].ans=j-i+1;
            }
            sum-=i*i;
            i++;
        }
        else{
            j++;
            sum+=j*j;
        }
    }
    cout<<cnt<<endl;
    for(ll i=1;i<=cnt;i++){
        cout<<a[i].ans<<" ";
        for(ll j=a[i].l;j<=a[i].r;j++){
            cout<<j<<" ";
        }
        cout<<endl;
    }
    return 0;
}

poj3320 Jessica’s Reading Problem

nefu 1517 林大实验林场–尺取法

1.和字符串那个题差不多,一本书有n页,求包含所有知识点的最短区间。
2.和1代码一模一样。

#include <bits/stdc++.h>
using namespace std;
int n,a[1000100],b[1000100],vis[1000100],cnt;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[a[i]]++;
        if(b[a[i]]==1) cnt++;
    }
    int i=1,j=1;
    int sum=1,ans=0x3f3f3f3f;
    vis[a[1]]++;
    while(i<=j&&j<=n){
        if(sum>=cnt){
            ans=min(ans,j-i+1);
            vis[a[i]]--;
            if(vis[a[i]]==0) sum--;
            i++;
        }
        else{
            j++;
            vis[a[j]]++;
            if(vis[a[j]]==1) sum++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

完结。
5.20快乐呀!!呜呜呜
在这里插入图片描述
在这里插入图片描述
第一次AK一遍全过,太鸡冻了,呜呜呜
纪念一下不过分吧。

猜你喜欢

转载自blog.csdn.net/qq_45859188/article/details/106179518