zoukankan      html  css  js  c++  java
  • 洛谷P1056_排座椅 贪心+桶排思想

    洛谷P1056_排座椅

    凡是最优解“最少情况”,一定利用贪心或者dp,本题贪心即可解决。

    分别开两个数组x,y,其中x[1]存储通道在第一行时可以隔开的对数,x[2]存储通道在第二行时可以隔开的对数,……y[1]存储通道在第一列时可以隔开的对数,y[2]存储通道在第二列时可以隔开的对数,……

    怎么求这个对数呢?没有必要按顺序搞个二重循环依次求出x[i]然后i++每次扫一遍struct数组,既然x,y数组只是计数器,那么跟他们求出的顺序无关。可以直接在输入数据时处理,若xi==pi,则同行不同列,特判yi与qi大小,若yi>qi,y[qi]++;反之y[yi]++;若yi==qi,则同列不同行,特判xi与pi大小,若xi>pi,则x[pi]++;反之x[xi]++;特判大小时可以使用min函数。

    最后利用桶排序,得到x,y数组中前k大和前l大的数的下标即为所求

     关于桶排序,在算法竞赛中详述。

    实际编程过程中,为了防止洛谷IDE把x[1005]与struct chat中的x混淆,把x,y数组改成a,b数组;

    以下是AC代码:

     1 #include<iostream>
     2 using namespace std;
     3 const int D=2005;
     4 int a[1005]={};  //row
     5 int b[1005]={};  //column
     6 int c[1005]={},o[1005]={};
     7 struct chat{
     8     int x,y;
     9     int p,q;
    10 }g[D];
    11 int main(){
    12     int m,n,k,l,d;
    13     cin >> m >> n >> k >> l >> d;
    14 //行相同列不同b,列相同行不同a 
    15     for(int i=1;i<=d;i++){
    16         cin >> g[i].x >> g[i].y >> g[i].p >> g[i].q;
    17         if(g[i].x==g[i].p){
    18             if(g[i].y<g[i].q)
    19                 b[g[i].y]++;
    20             else  b[g[i].q]++;
    21         }
    22         else if(g[i].y==g[i].q){
    23             if(g[i].x<g[i].p)
    24                 a[g[i].x]++;
    25             else  a[g[i].p]++;
    26         }
    27     }
    28 //bucketsort
    29     for(int i=1;i<=k;i++){
    30         int maxn=-1;   //用于找第i大的值 
    31         int p;           //用于记录第i大的值的下标 
    32         for(int j=1;j<m;j++){
    33             if(a[j]>maxn){
    34                 maxn=a[j];
    35                 p=j; 
    36             }
    37         }
    38         a[p]=0;
    39         c[p]++;
    40     }
    41     for(int i=1;i<=l;i++){
    42         int maxn=-1;
    43         int r;
    44         for(int j=1;j<n;j++){
    45             if(b[j]>maxn){
    46                 maxn=b[j];
    47                 r=j;
    48             }
    49         }
    50         b[r]=0;
    51         o[r]++;
    52     }
    53 //行尾没有空格 
    54     for(int i=1;i<=m-1;i++){
    55         if(c[i])
    56             cout << i << " ";
    57     }
    58     cout << endl;
    59     for(int i=1;i<=n;i++){
    60         if(o[i])  cout << i << " ";
    61     }
    62     cout << endl;
    63     return 0;
    64 

    注意:在输入的同时如果能够进行所有的处理(不需要后续处理诸如对原数据进行排序之类),为了节省空间没必要开结构体数组,因为数据用一次即可。

    下面是luogu用户的题解,我觉得写得非常简洁。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std; 
     7 int m,n,k,l,d;//变量名最好起与题目一致的 
     8 int x[1005],y[1005];//横纵坐标数组 
     9 int c[1005],o[1005];//桶排要用的数组
    10 int main() {
    11     scanf("%d%d%d%d%d",&m,&n,&k,&l,&d); 
    12     for(int i=1;i<=d;i++) { 
    13         int xi,yi,pi,qi; 
    14         scanf("%d%d%d%d",&xi,&yi,&pi,&qi); 
    15         if(xi==pi) 
    16             x[min(yi,qi)]++;//表示隔开这两排的价值 
    17         else 
    18             y[min(xi,pi)]++; //记得取min,即过道与前一个坐标保持一致 
    19     } 
    20     for(int i=1;i<=k;i++){//开始桶排 
    21         int maxn=-1;//为了求出每次的最大值,需要每次扫一遍 
    22         int p; 
    23         for(int j=1;j<m;j++){ 
    24             if(y[j]>maxn){ 
    25                 maxn=y[j]; 
    26                 p=j; 
    27             } 
    28         } 
    29         y[p]=0;//求出max之后一定要记得清零!!否则无论排多少次都是一个答案 
    30         c[p]++;//桶排不解释 
    31     } 
    32     for(int i=1;i<=l;i++){ 
    33         int maxn=-1; 
    34         int p; 
    35         for(int j=1;j<n;j++){ 
    36             if(x[j]>maxn){ 
    37                 maxn=x[j]; 
    38                 p=j; 
    39             } 
    40         } 
    41         x[p]=0; //同上 
    42         o[p]++; 
    43     } 
    44     for(int i=0;i<1005;i++)//输出答案 
    45     { 
    46         if(c[i])//表示需要隔开这行 
    47             printf("%d ",i); 
    48     } 
    49     printf("
    "); 
    50     for(int i=0;i<1005;i++) 
    51     { 
    52         if(o[i]) 
    53             printf("%d ",i); //同上 
    54     } 
    55     return 0; 
    56 }

    差距:

    1. 不知道桶排序思想,因而不知道可以这样选择前k个

    2. 不知道在输入时处理数据可以完全不需要结构体数组啊!同时输入时是可以处理数据的啊!没说必须要输入完毕再处理,这种方法在“师座操作系统”中使用过,不过那个更为自然,因为模拟了命令行

    3. 不知道algorithm头文件中的函数用法。下面给出algorithm中可能常用的函数

    • fill函数

    fill()函数参数:fill(first,last,val);
    // first 为容器的首迭代器,last为容器的末迭代器,val为将要替换的值。

    举例:

    1 int a[200];
    2 fill(a, a+100, 1);

    注意:
    fill()中 ,它的原理是把那一块单元赋成指定的值,也就是说任何值都可以
    memset(),则是将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,即0 、-1.(如果是char型数组,则任意数都可以,因为memset是一个一个字节赋值的,而char正好1Byte,但是int是4Bytes,假如赋值为1,则a数组中每个字节都用转成二进制的1(即00000001)填充,那么一个int内就是00000001000000010000000100000001,用16进制表示即为0x01010101,就等于16843009,就完成了对一个int元素的赋值了。


    下面是memset函数详解

    首先要知道memset函数是对字节为单位进行赋值的;

    void *memset(void *s, int ch, size_t n);
    函数解释:将s中前n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
    其实这里面的ch就是ascii为ch的字符;
    将s所指向的某一块内存中的前n个 字节的内容全部设置为ch指定的ASCII
    /*************************************************************************************************************/
    memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体数组进行清零操作的一种最快方法[1] 

    如果用memset(a,1,20);(实际上与memset(a,1,5*sizeof(int))结果是一样的)就是对a指向的内存的20个字节进行赋值,每个都用ASCⅡ为1的字符去填充,转为二进制后,1就是00000001,占一个字节。一个INT元素是4字节,合一起是0000 0001,0000 0001,0000 0001,0000 0001,转化成十六进制就是0x01010101,就等于16843009,就完成了对一个INT元素的赋值了。

    /****************************************************************************************************************/
    清零是memset(a,0,sizeof(a));
    初始化为无穷大为memset(a,0x3f,sizeof(0x3f));
    下面谈谈无穷大的用法;

    0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。

    另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。

    最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。

    所以在通常的场合下,0x3f3f3f3f真的是一个非常棒的选择。

    因为char是1字节,memset是按照字节赋值的,相当于把每个字节都设为那个数,所以char型的数组可赋任意值,int是4个字节,当memset(,1,sizeof()); 1相当于ASSCII码的1,1转为二进制00000001,当做一字节,一字节8位,int为4字节,所以初始化完每个数为00000001000000010000000100000001 = 16843009;

    初始化最大值的方法:

    如果你想初始最大化,第一位为符号位,不能为1,剩下全是1,也就是7个1,1111111化为十六进制正好为0x7f,所以memset(,0x7f,sizeof());就可以了


    • swap函数

    这个函数其实一般都是我自己写,一般和qsort,mergesort函数搭配。特别注意如果需要结构体整体swap时应该structname* a,structname* b;

    但是int* a,int* b也不是不行,只是要写两句swap比较麻烦。

    swap的STL实现,但是没有学OOP好像看不太明白

    1 // TEMPLATE FUNCTION swap (from <algorithm>)
    2 template<class _Ty> inline
    3  void swap(_Ty& _Left, _Ty& _Right)
    4  { // exchange values stored at _Left and _Right
    5  _Ty _Tmp = _Move(_Left);
    6  _Left = _Move(_Right);
    7  _Right = _Move(_Tmp);
    8  }
    
    • sort函数

    sort函数有三个参数:

    (1)第一个是要排序的数组的起始地址。
    (2)第二个是结束的地址(最后一位要排序的地址的下一地址)
    (3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。
    Sort函数使用模板:
    Sort(start,end,排序方法)
    //降
    bool cmp1(int a,int b){
        return a>b;
    } 
    //升
    bool cmp2(int a,int b){
        return a<b;
    }
    sort(a,a+10);
    sort(a,a+10,cmp2);
    sort(a,a+10,cmp1);

    STL实现比较复杂,参见STL sort 函数实现详解

    还有很多其他函数,在下周学OOP的时候好好学啊!

  • 相关阅读:
    Ajax省市区无刷新单表联动查询
    Hadoop2.0、YARN技术大数据视频教程
    零基础DNET B/S开发软件工程师培训视频教程
    零基础DNET CS开发视频教程
    HTML5开发框架PhoneGap实战视频教程
    Web前端开发视频教程
    FluentData 轻量级.NET ORM持久化技术详解
    前端 MVVM 框架KnockOut.JS深入浅出视频教程
    ASP.NET Web开发项目实战视频教程
    零基础到CS开发高手通用权限管理系统全程实录
  • 原文地址:https://www.cnblogs.com/horizonlc/p/10355775.html
Copyright © 2011-2022 走看看