zoukankan      html  css  js  c++  java
  • 选择客栈(巧妙)

    题面:https://www.luogu.com.cn/problem/P1311

    丽江河边有 nn 家很有特色的客栈,客栈按照其位置顺序从 11 到 nn 编号。每家客栈都按照某一种色调进行装饰(总共 kk 种,用整数 0 sim k-10k1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。

    两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 pp 。

    他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 pp 元的咖啡店小聚。

    样例:

    5 2 3 
    0 5 
    1 3 
    0 2 
    1 4 
    1 5 


    写出这题还是比较得意的,虽然中途下载了测试数据,不过思想是自己的。

    自己的思想O(nk)

    Ⅰ.我们这样想,如果一个客栈的右边(包括自己)都是花费大于P的,那么这个客栈的右边一定不存在和这个客栈匹配的。

    Ⅱ.如果某个客栈 i 和右边的同色客栈 j 符合条件的话,那么他们之间肯定存在一个花费低于P的客栈

    Ⅲ.在Ⅱ的基础上, j 右边的同色客栈一定能和i组成符合条件的。

    Ⅳ.综上所诉,我们只关系客栈右边第一个价值符合条件的客栈位置,以及该符合条件的客栈(包括该客栈)右边有几个同色客栈。

    ②实现

    Ⅰ.客栈右边第一个价值低于P客栈的位置,我们直接倒序枚举。

    初始令ans=-1表示右边没有合适的客栈了

    然后发现一个符合条件的客栈就更新ans的位置

    int ans=-1;//表示没有 
    for(int i=n;i>=1;i--)
    {
        if(hua[i]<=p)    ans=i;//更新 
        jin[i]=ans;
    }

    Ⅱ.对于颜色统计,仍然倒序枚举,用vis数组统计颜色情况。

    sumn [ i ] [ j ] 表示在 i 客栈后面(包括 i 客栈)有 j 颜色的客栈多少个

    for(int i=n;i>=1;i--)
    {
        vis[color[i]]++;//统计当前颜色 
        for(int j=0;j<=k;j++)
            sumn[i][j]=vis[j];//把所有颜色装进去 
    }

    代码如下

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=200009;
    int n,k,p;
    int color[maxn],hua[maxn],vis[59];
    int jin[maxn];//从i往后,最快遇到得廉价酒馆
    int sumn[maxn][59];//从i往后,不包括i与i同色得酒馆 
    int main()
    {
        scanf("%d%d%d",&n,&k,&p);
        for(int i=1;i<=n;i++)    scanf("%d%d",&color[i],&hua[i]);
        int ans=-1;
        for(int i=n;i>=1;i--)
        {
            if(hua[i]<=p)    ans=i;
            jin[i]=ans;
            vis[color[i]]++;
            for(int j=0;j<=k;j++)
                sumn[i][j]=vis[j];
        }
        int cnt=0;
        for(int i=1;i<=n;i++)//O(n)扫一遍
        {
            if(jin[i]==-1)    continue;
            //右边没有价值更低的客栈了,continue 
            cnt+=sumn[jin[i]][color[i]];
            if(jin[i]==i)    cnt--;
            //如果自己就是距离最近的廉价客栈
            //那么sumn[jin[i]][color[i]]多算了一次
            //自己是不可以和自己匹配的 
        } 
        cout<<cnt;
    }    
    View Code

    不过,令人伤心的是,还有更快的O(n)算法。

     箭头代表该客栈是小于花费P的客栈

    那么,当我们枚举到第二个客栈时,发现这是在当前枚举过程中最后出现的一个符合花费的客栈,我们把他的下标记作 t

     所以后面的客栈总可以和第二个客栈前面的同色客栈形成一对

    然后我们枚举到第四个客栈时,更新t

    从第四个客栈开始起,所有后续客栈都可以和4之前的同色客栈形成一对,而不是仅仅和2之前的同色客栈形成一对。

    所以奉上代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=500009;
    int n,k,p,last[maxn],sumn[maxn];
    int color,price,vis[59];
    //vis记录颜色 
    int main()
    {
        cin>>n>>k>>p;
        int ans=0,now;
        for(int i=1;i<=n;i++)
        {
            cin>>color>>price;
            if(price<=p)    now=i;
            //now记录目前最后符合条件的客栈编号 
            if(now>=last[color])//如果大于上一个颜色出现的位置
                sumn[color]=vis[color];//那么不仅仅是2之前的,现在是4之前的 
            last[color]=i;//最后一次出现在i
            ans+=sumn[color];
            vis[color]++; 
        }
        cout<<ans; 
    } 
    View Code
  • 相关阅读:
    实现类似add(1)(2)(3)的函数
    Chrome安装助手踩坑
    升级webpack4错误处理
    vue项目埋点
    如何理解vue中的v-bind?
    不能不知道的webpack基本配置
    IE9及以下浏览器升级提示
    HTML5常用API
    css中clip属性
    Web开发展望
  • 原文地址:https://www.cnblogs.com/iss-ue/p/12494740.html
Copyright © 2011-2022 走看看