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 }