• 2016.7.15.2014noip模拟赛D1(和昨天那个不一样,但同样网上搜不到


    1.      合理种植

    (plant.pas/.c/.cpp)

    【问题描述】

       大COS在氯铯石料场干了半年,受尽了劳苦,终于决定辞职。他来到表弟小cos的寒树中学,找到方克顺校长,希望寻个活干。

          于是他如愿以偿接到了一个任务……

    美丽寒树中学种有许多寒树。方克顺希望校园无论从什么角度看都是满眼寒树,因此他不希望有三棵甚至更多寒树种在一条直线上。现在他把校园里n棵寒树的坐标都给了大COS,让他数出存在多少多树共线情况。(若一条直线上有三棵或以上的树,则算出现一个多树共线情况。)

    【输入】

          输入文件名为plant.in。

          第1行一个正整数n,表示寒树棵数。

          接下来n行,每行两个非负整数x、y,表示一颗寒树的坐标。没有两颗寒树在同一位置。

    【输出】

          输出文件名为plant.out。

          输出一个整数,表示存在多少多树共线情况。

    【输入输出样例】

    plant.in

    plant.out

    6

    0 0

    1 1

    2 2

    3 3

    0 1

    1 0

    1

    【数据范围】

          对于30%的数据,有n≤10;

          对于50%的数据,有n≤100;

          对于100%的数据,有n≤1,000,0≤x,y≤10,000。

    解析:

    槐花槐花,我会说我第一遍读题居然直接没有管“无论什么角度”这几个字吗?宛如一个智障,结果只算了0,45,90,135度的情况,得了十分......咳咳,然后,下午想了一会,用了另一个o(N3)的方法,险过,再加一句绝对TLE,因为有人借用我的方法改了一些地方就超时了。

    我的方法:o(N3

    先用N2,把i点与j点所在直线的斜率计算出来,存入long long  f[i][j]中,为什么不用double?原因很简单,我不太清楚double存数据的方式,后来在网上查了一下,发现double也不行,因为double存的是23位,如果10000除以两个小质数,其斜率可能存储相等,因为10000/9999和9999/9998所得数是在小数点后第8位才有区别的。所以我用的longlong,当然我还*108,解决。然后再来一个N3的循环,枚举三个点,再用一个bool a[i][j]表示i和j所在直线三点共线的情况算了没有。此处,如果你在后面的循环内再计算斜率->TLE。当然你也可以不采用k=y/x的公式,用高中学的(x1-x3)(y2-y3)=(x2-x3)(y1-y3)这个式子也可以,但是同样的不可以放在后面的循环中,放了,最多7个点,剩下500以上就TLE。

    程序:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int n,x,y,ans;
     6 struct stu 
     7 {
     8     int x,y;
     9 }tree[1005];
    10 long long X,Y,k;
    11 long long dis[1005][1005];
    12 bool a[1005][1005];
    13 int main()
    14 {
    15     freopen("plant.in","r",stdin);
    16     freopen("plant.out","w",stdout);
    17     scanf("%d",&n);
    18     for(int i=1;i<=n;i++)
    19     {
    20         scanf("%d %d",&tree[i].x,&tree[i].y);
    21     }
    22     for(int i=1;i<=n;i++)
    23        for(int j=i+1;j<=n;j++)
    24         {
    25             Y=tree[i].y-tree[j].y;
    26             X=tree[i].x-tree[j].x;
    27             if(X!=0) k=(Y*100000000LL)/X;
    28             else k=1;
    29             dis[i][j]=k;
    30         }
    31     for(int i=1;i<=n;i++)
    32        for(int j=i+1;j<=n;j++)
    33     {
    34         
    35           for(int l=j+1;l<=n;l++)
    36           if(dis[i][j]==dis[j][l])
    37           {
    38               if(a[i][j]||a[j][l]||a[i][l]) 
    39               {
    40                   a[i][j]=a[j][l]=a[i][l]=true;
    41               }
    42               else 
    43             {
    44                   ans++;
    45                   a[i][j]=a[j][l]=a[i][l]=true;
    46               }
    47           }
    48     }
    49     cout<<ans<<endl;
    50     return 0;
    51 }

    正解:o(N2logN)

    半小时内真心看不懂,看的朕心力交瘁,肾虚不治。最后还是老师点破天机。大概思路是先算每两个点的横坐标差、纵坐标差并保存,然后存个方向(bool),就是斜率为正还是为负。就这个方向,解决掉了多点共线重复计算的问题。而就是这个方向,我看代码一直没明白,唉,作为一个学水。建议理解为向量,这样向量的方向是x、y、左右有关的。这样理解要简单一点点。以及最后要用斜率排序,让斜率相同的靠在一起,这样才方便连着算。

    然后再解释一下方向判重问题,如图所示:

    这个样子到了第二个点的时候1-2与2-3的方向不同,就不会再计算后面的了。

     1 #include <fstream>
     2 #include <cstdlib>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 ifstream fin("plant.in");
     7 ofstream fout("plant.out");
     8 long n;
     9 struct xxx{long x,y;} a[1005];
    10 struct kk{long dx,dy;bool c;} k[1005];
    11 bool operator <(kk k1,kk k2){
    12     if (k1.dx==10001) return k2.dx<10001;
    13     if (k2.dx==10001) return 0;
    14     return k2.dx*k1.dy<k1.dx*k2.dy;
    15 }
    16 bool operator ==(kk k1,kk k2){
    17     if (k1.dx==10001||k2.dx==10001)
    18         return k1.dx==k2.dx;
    19     return k2.dx*k1.dy==k1.dx*k2.dy;
    20 }
    21 void init(){
    22     long i;
    23     fin>>n;
    24     for (i=0;i<n;i++)
    25         fin>>a[i].x>>a[i].y;
    26 }
    27 void tr(){
    28     long i,j,s,ans=0;
    29     bool f;
    30     for (i=0;i<n;i++)
    31     {
    32         for (j=0;j<n;j++)
    33             if (i!=j)
    34                 if (a[i].x==a[j].x)
    35                 {
    36                     k[j].dx=10001;
    37                     k[j].c=a[i].y<a[j].y;
    38                 }
    39                 else
    40                 {
    41                     k[j].dx=a[j].x-a[i].x;
    42                     k[j].dy=a[j].y-a[i].y;
    43                     if (k[j].dx<0)
    44                     {
    45                         k[j].dx=-k[j].dx;
    46                         k[j].dy=-k[j].dy;
    47                     }
    48                     k[j].c=a[i].x<a[j].x;//判断j在i左还是右 
    49                 }
    50         for (j=i;j<n-1;j++) k[j]=k[j+1]; 
    51         sort(k,k+n-1);//以斜率大小为基准排,这样斜率相同就方便计算 
    52         f=1; s=0;
    53         for (j=0;j<n-2;j++)
    54         {
    55             s++;
    56             f=f&&k[j].c;
    57             if (!(k[j]==k[j+1]))
    58             {
    59                 if (f&&s>1) ans++;//若方向相同且有三个点以上
    60                 f=1; s=0;
    61             }
    62         }
    63         if (f&&k[n-2].c&&s>0) ans++;
    64     }
    65     fout<<ans;
    66 }
    67 int main(){
    68     init();
    69     tr();
    70     fin.close();
    71     fout.close();
    72     return 0;
    73 }

    2.      排队

    (lineup.pas/.c/.cpp)

    【问题描述】

          小sin所在的班有n名同学,正准备排成一列纵队,但他们不想按身高从矮到高排,那样太单调,太没个性。他们希望恰好有k对同学是高的在前,矮的在后,其余都是矮的在前,高的在后。如当n=5,k=3时,假设5人从矮到高分别标为1、2、3、4、5,则(1,5,2,3,4)、(2,3,1,5,4)、(3,1,4,2,5)都是可行的排法。小sin想知道总共有多少种可行排法。

    【输入】

          输入文件名为lineup.in。

          一行两个整数n和k,意义见问题描述。

    【输出】

          输出文件名为lineup.out。

          输出一个整数,表示可行排法数。由于结果可能很大,请输出排法数mod 1799999的值。

    【输入输出样例】

    lineup.in

    lineup.out

    5 3

    15

    【数据范围】

          对于20%的数据,有n≤10,k≤40;

          对于60%的数据,有n≤100,k≤500;

          对于100%的数据,有n≤100,k≤n*(n-1)/2。

    正解:找规律,简单,上代码。(当然,你没找到规律就傻眼儿吧)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int p=1799999;
    int n,k,a[105][5000];
    int main()
    {
        freopen("lineup.in","r",stdin);
        freopen("lineup.out","w",stdout);
        scanf("%d %d",&n,&k);
        for(int i=2;i<=n;i++)
        {a[i][1]=i-1; a[i][0]=1; a[i][i*(i-1)/2]=1;}
        for(int i=3;i<=n;i++)
           for(int j=2;j<(i*(i-1)/2);j++)
           {
               for(int z=j;z>=j-i+1&&z>=0;z--)
           {
               a[i][j]=(a[i-1][z]+a[i][j])%p;
           }
           }
        
        cout<<a[n][k]<<endl;
        return 0;
    }

    1.      科技节

    (scifest.pas/.c/.cpp)

    【问题描述】

          一年一度的科技节即将到来。同学们报名各项活动的名单交到了方克顺校长那,结果校长一看皱了眉头:这帮学生热情竟然如此高涨,每个人都报那么多活动,还要不要认真学习了?!这样不行!……于是,校长要求减少一些活动,使每位学生只能参加一项(一名同学要参加某活动,必须已报名且该活动未被去掉)。当然,他也不希望哪位同学因此不能参加任何活动。他想知道自己的方案能否实行。

    【输入】

          输入文件名为scifest.in。

          输入数据包括多组。

          对于每组数据:

          第一行两个正整数n和m,分别表示活动数和学生数。

          接下来n行,每行m个为0或1的数。第i+1行第j列的数若为1,表示j同学报名参加活动i,否则表示j同学没有报名参加活动i。

    【输出】

          输出文件名为scifest.out。

    对于每组数据输出一行,若校长方案可行则输出“Yes”,否则输出“No”。(均不包括引号)

    【输入输出样例】

    scifest.in

    scifest.out

    3 3

    0 1 0

    0 0 1

    1 0 0

    4 4

    0 0 0 1

    1 0 0 0

    1 1 0 1

    0 1 0 0

    Yes

    No

    【数据范围】

          对于20%的数据,n≤10,m≤200,数据组数≤10;

          对于60%的数据,n≤16,m≤300,数据组数≤100;

          对于100%的数据,n≤16,m≤300,数据组数≤1,000。

     正解:位运算,针对活动来解决。优化是从人多的那个活动开始补。我觉得不如理解为拼图:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int n,m,a[20][10];
     6 int h[20],c,k;
     7 int o;
     8 void can(int act,int x[10],int students)
     9 {
    10      if(act==n)
    11      {
    12          if(students==m) o=1;
    13          return ;
    14      }
    15      int bili,t[10];
    16      for(bili=0;bili<10;bili++)
    17      {
    18          if((x[bili]&a[act][bili])>0) break;
    19          t[bili]=x[bili]|a[act][bili];
    20      }
    21      if(bili==10) can(act+1,t,students+h[act]);
    22      if(!o) can(act+1,x,students);//取了没用就不取 
    23 }
    24 int main()
    25 {
    26     freopen("scifest.in","r",stdin);
    27     freopen("scifest.out","w",stdout);
    28     while(~scanf("%d %d",&n,&m))
    29     {
    30         
    31         memset(a,0,sizeof(a));
    32         for(int i=0;i<n;i++)
    33         {
    34             h[i]=k=0;
    35             for(int j=0;j<m;j++)
    36            {
    37                scanf("%d",&c);
    38                if(c)
    39                {
    40                    a[i][k/31]+=1<<(k%31);
    41                    h[i]++;
    42                }
    43                k++;
    44            }
    45         }
    46     for (int i=0;i<n-1;i++)
    47         for (int j=i+1;j<n;j++)
    48             if (h[i]<h[j])
    49             {
    50                 k=h[i]; h[i]=h[j]; h[j]=k;
    51                 for (int l=0;l<10;l++)
    52                 {
    53                     k=a[i][l]; a[i][l]=a[j][l]; a[j][l]=k;
    54                 }
    55             }
    56         o=0;
    57         can(0,a[19],0);
    58         if(o) cout<<"Yes"<<endl;
    59         else cout<<"No"<<endl;
    60     }
    61     return 0;
    62 }
  • 相关阅读:
    学习ActiveMQ(五):activemq的五种消息类型和三种监听器类型
    学习ActiveMQ(三):发布/订阅模式(topic)演示
    学习ActiveMQ(二):点对点(队列)模式消息演示
    Linux系统基础优化及常用命令
    【原创】Access自动编号的初始值设置及重置编号
    【整理】引用类型与ref传递实例精解
    【原创】数据库操作类库整理
    【摘录】Random快速产生相同随机数的原因及解决方案
    【整理】辗转相除法求最大公约数算法证明
    【转载】access采用sql语句与msql的区别
  • 原文地址:https://www.cnblogs.com/fisch/p/5674851.html
Copyright © 2020-2023  润新知