重温经典第二弹(xdoj1175,xdoj1179)

18 篇文章 0 订阅
13 篇文章 0 订阅

一转眼,记忆又来到了暑假。或许,这是一个这算是自己真正开始接触了解acm的一个时间点吧,各种算法数据结构,开始慢慢浮出水面。回顾当初,感慨万千。又找出了两道未ac之题,确实复杂度明显加强,思维性的进一步考验。


Count

 

 

思路:子串搜索问题,因为n和k大到2e5,因此,肯定是个单向处理不能回溯的问题,否则最坏n方的复杂度是难以接受的。

对于单次搜索,考虑可以维护现有区间的元素,然后移位遍历向后搜索,对于该区间是否满足条件,可以用map记录已有元素数量,若存在大于一的情况,则存在重复,并记录重复数。每次移位过程中,添加新增元素,去除退出的元素,若重复数为零,则满足该条件。

暴力搜索的思维确实是挺关键,而维护已有数据,则为一个比较难想到的点,以相关性较低的统计量代替原有值,实为一相当出色的思维

 

 

/*
Author:Owen_Q
*/

#include <bits/stdc++.h>

using namespace std;

const int maxn = 2e5+10;

int a[maxn];

map <int,int> m;

int main()
{
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        m.clear();
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        int sum=0;
        int same=0;
        for(int i=0;i<k;i++)
        {
            m[a[i]]++;
            if(m[a[i]]>1)
                same++;
        }
        if(same==0)
            sum++;
        for(int i=0;i<n-k;i++)
        {
            if(m[a[i]]>1)
            {
                same--;
            }
            m[a[i]]--;
            m[a[i+k]]++;
            if(m[a[i+k]]>1)
            {
                same++;
            }
            if(same==0)
            {
                sum++;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

 

 


Furude_Rika and coins

 

 

 

思路:这一题,确实考验对题中细节的把握。

由于s的值已定,则可以事先进行一波预处理。而s与n的值较小,时间又惊人的延长到10s,这无形中就在提醒我们暴力枚举的可能性。

不过,如果强行模拟两种钱币的情况,虽然将不受询问次数q的影响,不过O(n*n*s*s)的复杂度高达1e11,显然破产。

不过,如果只考虑一种钱币,就可观多了,O(n*s)的复杂度最坏也就5e5,哪怕加上q,也不过5e9,再考虑超长的时延,这还是一个相当安全的方式。

由于钱币总和不会大于最大询问钱数,因此可以根据询问情况,将钱数作为处理的对象数组,进行不断枚举更新。

于是,在数据传入之前,先提前处理一波单钱币情况,然后,在询问过程中,根据所给条件再遍历第二的钱币,根据总数得到,剩余的所需第一个钱币,并与预处理的结果相结合,就ac了

这题的难点就是将两种钱币分到两部分进行处理,同样是暴力枚举,却又各种不同的方法,学问很深啊。

其实,这题有点分组背包的味道,循环外处理单一钱币的情况,循环内处理两种情况的情况,由于不是每组选一个,而是只选一或两组,因此每次记录最小值,不更新原数组就好了

 

 

/*
Author:Owen_Q
*/

#include <bits/stdc++.h>

using namespace std;

const int maxa=5010;
const int maxn=1e7+10;

int a[maxa];
int num[maxn];

/*bool cmp(const int x,const int y)
{
    return x>y;
}*/

int main()
{
    int n,s,q,m;
    while(scanf("%d%d",&n,&s)!=EOF)
    {
        memset(num,s+1,sizeof(num));
        num[0]=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        scanf("%d",&q);
        //sort(a,a+n,cmp);

        for(int i=0;i<n;i++)
        {
            for(int k=1;k<=s;k++)
            {
                int temp=a[i]*k;
                if(num[temp]>k)
                    num[temp]=k;
            }
        }

        while(q--)
        {
            int sum;
            scanf("%d",&m);
            sum=num[m];
            for(int i=0;i<n;i++)
            {
                for(int j=1;j<=s;j++)
                {
                    if(m-a[i]*j<0)
                        break;
                    if(sum>num[m-a[i]*j]+j)
                        sum=num[m-a[i]*j]+j;
                }
            }
            if(sum>s)
                printf("%d\n",-1);
            else
                printf("%d\n",sum);
        }
    }
    return 0;
}

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值