尺取法

新学的东西,用水题来练练手。

下面是套路:

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;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值