POJ 2566-Bound Found (尺取法)

题意:给定n个数,m次询问,每次询问一个数k,求n个数内所有区间的区间和的绝对值,最接近k的那个(非空)区间的和的绝对值,输出该区间的和的绝对值和左右端点。

思路:很有趣的一个尺取法的题,先转化成求出所有的前缀和,然后排序,任意一个不包含1端点的区间,都可以用两个区间的前缀和的差表示。要加入含1端点的区间,即前缀的情况,要么特别判断,

也可以加入一组0点的值0,

感想 :很难受,没有注意到区间为0的情况,初始复制一直将l=r=1;....WA+10+。。。贼尴尬,调了一晚上。。。都没有发现。最后跟着题解一点点分析,才发现的,,,

代码1:(自己的,但是加入包含1点的区间情况特判,但显得有点蠢,,)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;///!!!不能是空集
ll n,m,k,a[100005],l,r,s,ans,ansk,ansl,ansr;
struct AA
{
    ll sum,r;
    bool operator<(const AA &aa)const
    {
        return sum<aa.sum;
    }
}pos[100010];
int main()
{
    pos[0].sum=0;
    pos[0].r=0;
    while(scanf("%lld%lld",&n,&m))
    {
        if(n==0&&m==0) break;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]),pos[i].sum=pos[i-1].sum+a[i],pos[i].r=i;
        }
        sort(pos+1,pos+1+n);
        while(m--)
        {
            scanf("%lld",&k);
            l=1;r=2,ansk=99999999999;
            while(1)
            {
                if(abs(abs(pos[l].sum)-k)<ansk)
                {
                    ans=abs(pos[l].sum);
                    ansk=abs(ans-k);
                    ansl=0;
                    ansr=pos[l].r;
                }
                for(;r<=n;r++)
                {
                    if(abs(pos[r].sum-pos[l].sum-k)<ansk)
                    {
                        ans=abs(pos[r].sum-pos[l].sum);
                        ansk=abs(ans-k);
                        ansl=pos[r].r;
                        ansr=pos[l].r;
                    }
                    if(pos[r].sum-pos[l].sum>k)
                    {
                        break;
                    }
                }
                l++;
                if(l==r) r++;
                if(l>n) break;

            }
            if(ansl>ansr) swap(ansl,ansr);
            printf("%lld %lld %lld\n",ans,ansl+1,ansr);
        }
    }
}

代码2:(网上找的题解,但是加入1的方法,就是通过加一个0点,代码显得很简洁,整齐,优秀~)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=1e5+5;
const int INF=2147483640;
pair<int,int> sum[MAXN];
int n,k,t;

void init()
{
    sum[0]=make_pair(0,0);
    int tmp=0;
    for (int i=1;i<=n;i++) 
    {
        int x;
        scanf("%d",&x);
        tmp+=x;
        sum[i]=make_pair(tmp,i);
    }
    sort(sum,sum+n+1);     
}

void solve()
{
    scanf("%d",&t);
    int l=0,r=1,minans=INF,ans,ansl,ansr;
    while (r<=n && minans)//这里一开始写成了ans,以后变量名不要取那么相像orz 
    {
        int delta=sum[r].first-sum[l].first;
        if (abs(delta-t)<=minans)
        {
            minans=abs(delta-t);
            ans=delta;
            ansl=sum[l].second;
            ansr=sum[r].second;
        }
        if (delta<t) r++; 
        if (delta>t) l++;
        if (l==r) r++;//☆注意序列不能为空!
    }
    if (ansl>ansr) swap(ansl,ansr);//注意排序后是无序的,左右区间要调整回有序 
    printf("%d %d %d\n",ans,ansl+1,ansr); 
}

int main()
{
    while (scanf("%d%d",&n,&k)!=EOF) 
    {
        init();
        for (int i=1;i<=k;i++) solve();
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值