尺取法_学习笔记

所谓的尺取法并不是像尺子那样固定刻度距离来去不断的比对得出的结果,而是一种抽象的根据题目要求选取比量标准的“尺子”。

还是通过例题来理解:

POJ 3061 Subsequence http://poj.org/problem?id=3061

题意:(摘自《挑战》)给定长度为n的数列整数 a0 a1 a2 .... an-1 以及整数S . 求出总和不小于S的连续子序列的长度的最小值。如果不存在,则输出0。

10< n < 10^5

0  < ai <= 10^4

S  < 10^8

我们通过分析可以得出如下结论:

要: as + ... +at-1 <S

则 :as+1  +  ...  + at-2 < as + ... +at-2 <S  ==》  as+1 + ... + at'-1 >=S  那么 t' >= t

因此我们可以:

(1) 以s=t=sum=0 初始化

(2)只要依然有sum<S 就不断的将sum增加 at ,并将 t 增加1

(3)如果(2)中无法满足sum >= S 则终止。否则的话,更新 res = min(res, t-s)

(4)将sum减去 as , s增加1然后回到(2)

 这个算法因为 t 只可能最多改变 n 次所以只需O(n) 的复杂度就可以求解这个问题了。

#include <iostream>
#include <cstdio>
using namespace std;
int t;
int n,m;
int a[100005];
void solve()
{
    int res=n+1;
    int s=0,t=0,sum=0;
    while(1)
    {
        while(t<n&&sum<m)
        {
            sum+=a[t++];
        }
        if(sum<m) break;
        res=min(res,t-s);
        sum-=a[s++];
    }
    if(res>n) res=0;
    cout<<res<<endl;
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=0;i<n;i++) cin>>a[i];
        solve();
    }
    return 0;
}

 Black&White http://icpc.upc.edu.cn/problem.php?cid=1720&pid=5

题目描述

你有一个长度为 n 的 01 串S,你可以执行最多 m 次操作。
对于每次操作,你可以选择一个位置 i 满足 1≤i≤n,翻转这一位的值,0变成1,1变成0。
定义一个 01 串的价值为其中最长连续0的个数和最长连续1的个数的较大值,求S在经过最多m次操作后的最大价值。

输入

第一行一个整数 T ,表示接下来有 T 个样例。
首先输入n,m,表示S串的长度n和操作次数m,其中1≤n≤100000,0≤m≤1000;
接下来输入一个长度为n的字符串S。

输出

一个整数,表示题面上描述的最大价值。

样例输入

复制样例数据

2
5 1
00101
2 1
01

样例输出

4
2

提示

第一个串翻转第三个位置,00001的价值为4;第二个串翻转第一个位置,11的价值为2。

【分析】我们很容易想到的一种解法分别对串进行两次操作一是翻转1,而是翻转0 。对于每一次操作,固定首位 s 然后 t 不断的向后走,遇到和目标改变字符不相同的m-- 直到 m 减成 0 ,然后 s++. 然而这样的做法复杂度无疑会很大:

那么我们就换一种思路把s++ 和 t++ m-- 进行合并。也就以后了如下的代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <math.h>
#include <string>
#include <cstring>
using namespace std;
int n;
int m;
int t;
string str;
int num[100005];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        char ch;
        scanf("%d%d",&n,&m);
        int n1=0,n0=0;
        getchar();
        for(int i=0;i<n;i++)
        {
            scanf("%c",&ch);
            num[i]=ch-'0';
            if(num[i]==0) n0++;
            if(num[i]==1) n1++;
        }
        if(n0<=m||n1<=m)
        {
            printf("%d\n",n);
            continue;
        }
        int l=-1,r=-1,res=0;
        int rot=0;
        while(r<n)
        {
            if(rot<=m)
            {
                r++;
                if(num[r]==1) rot++;
                if(rot>m) continue;
                res=max(res,r-l);
            }
            else
            {
                l++;
                res=max(res,r-l);
                if(num[l]==1) rot--;
            }
        }
        l=-1,r=-1;
        rot=0;
        while(r<n)
        {
            if(rot<=m)
            {
                r++;
                if(num[r]==0) rot++;
                if(rot>m) continue;
                res=max(res,r-l);
            }
            else
            {
                l++;
                res=max(res,r-l);
                if(num[l]==0) rot--;
            }
        }
        cout<<res<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值