zoukankan      html  css  js  c++  java
  • NOIP2011 选择客栈

    题目描述

    丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从1 到n 编号。每家客栈都按照
    某一种色调进行装饰(总共k 种,用整数0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每
    家咖啡店均有各自的最低消费。
    两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定
    分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于
    两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过p。
    他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过p
    元的咖啡店小聚。
    【输入输出样例说明】
    客栈编号 ① ② ③ ④ ⑤
    色调 0 1 0 1 1
    最低消费 5 3 2 4 5
    2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈①③,②④,②⑤,④⑤,
    但是若选择住4、5 号客栈的话,4、5 号客栈之间的咖啡店的最低消费是4,而两人能承受
    的最低消费是3 元,所以不满足要求。因此只有前3 种方案可选。
    【数据范围】
    对于 30%的数据,有n≤100;
    对于 50%的数据,有n≤1,000;
    对于 100%的数据,有2≤n≤200,000,0<k≤50,0≤p≤100, 0≤最低消费≤100。

    输入格式

    第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色
    调的数目和能接受的最低消费的最高值;
    接下来的 n 行,第i+1 行两个整数,之间用一个空格隔开,分别表示i 号客栈的装饰色
    调和i 号客栈的咖啡店的最低消费。

    输出格式

    输出只有一行,一个整数,表示可选的住宿方案的总数。

    样例输入

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

    样例输出

     3

    O(N*N)方法:记录各个点的c[i],v[i],然后访问每个点,每个点再跟后面的点比较,相同颜色的+1,比P大的+0。
    O(N*K)方法:上面方法的优化
    f[i,j]表示在前i个点中,有颜色j的客栈的数量。 当我们读入C,V后,必须把0 到 K-1点中更新一下。
    但是如果我们找到V比P来的大的话,怎么办?我们就要分类讨论,第一种:把总数加上在该点前面相同颜色的个数,即
    s:=s+f[i,c]-1(减1的原因是因为f[i,c]记录了该点加上前面的点);第二种:s:=s+f[前面小于等于V的点,c];最后输出S;

     1 var f:array[0..200010,0..50] of longint;
     2       a:array[0..200000] of longint;
     3       i,j,k,n,p,c,v,s:longint;
     4 
     5 begin
     6   assign(input,'hotel.in'); reset(input);
     7   assign(output,'hotel1.out'); rewrite(Output);
     8   readln(n,k,p);
     9   for i:=1 to n do
    10    begin
    11      readln(c,v);
    12      for j:=0 to k-1 do
    13       if j=c then f[i,j]:=f[i-1,j]+1 else f[i,j]:=f[i-1,j];
    14      if v<=p then
    15       begin
    16         a[i]:=i;
    17         s:=s+f[i,c]-1;
    18       end else
    19     begin
    20      a[i]:=a[i-1];
    21      s:=s+f[a[i],c];
    22     end;
    23   end;
    24   writeln(s);
    25   close(input); close(output);
    26 end.
    View Code


    貌似可以再优化下
    O(n)方法: 这种方法在时空复杂度,编程复杂度来说都比前面方法来的简单。
    我们可以开3个数组:a,b,d。
    读入C,V,
    a[c]前面以及该点的点中颜色为c的客栈数,b[c]前面以及该点的点中颜色为c且(符合消费或者相差距离不为1)的客栈数,d[c]为 上一个颜色c的客栈编号, 如果到该点最近的符合消费的客栈不小于上一个颜色c的客栈编号(d[c]),就b[c]:=a[c]。
    最后记得把总数加上b[c],a[c]+1,更新d[c]:=i; 输出S;

     1 var a,b,d:array[0..60] of longint;
     2 n,k,p,i,c,v,f,s:longint;
     3 
     4 begin
     5 assign(input,'hotel.in'); reset(input);
     6 assign(output,'hotel.out'); rewrite(output);
     7 readln(n,k,p);
     8 for i:=1 to n do
     9 begin
    10 readln(c,v);
    11 if v<=p then f:=i;
    12 if f>=d[c] then b[c]:=a[c];
    13 s:=s+b[c];
    14 inc(a[c]);
    15 d[c]:=i;
    16 end;
    17 writeln(s);
    18 close(input); close(Output);
    19 end.
    View Code


    还有一种O(nlogn)的方法:用list[i,j]表示颜色为i的第j个客栈,也就是将客栈按照颜色紧缩存储。另用pos[i]表示第i个旅馆在list [color[i]]中的位置。用线段树/ST算法(推荐)预处理出区间消费的最小值,也就是min{cost[i..j]},易得到 min[k,i]是非增的,注意这是后面二分的关键。然后枚举第二个人,在list[color[i]]中用二分找到一个j满足 min[j,i]<=P,那么ans=ans+j,因为list[color[i],1..j]中必然都是颜色为color[i],且区间最小值也都<=P。

  • 相关阅读:
    c#自动更新+安装程序的制作
    VS2013项目受源代码管理向源代码管理注册此项目时出错
    WinDbg配置和使用基础
    InstallShield Limited Edition for Visual Studio 2013 图文教程(教你如何打包.NET程序)
    PowerDesigner 如何生成数据库更新脚本
    用户故事(User Story)
    Troubleshooting Record and Playback issues in Coded UI Test
    Coded UI
    compare two oracle database schemas
    How to: Use Schema Compare to Compare Different Database Definitions
  • 原文地址:https://www.cnblogs.com/oxxxo/p/3370853.html
Copyright © 2011-2022 走看看