首页 > 代码库 > 第六周周赛题解
第六周周赛题解
A题
这题贼水,直接暴力就可以了。
用个bool数组记录一下,如果某一天,当前剩下的最大的出现了的话,就输出一段。
1 #include<stdio.h> 2 int n; 3 bool vis[100010]; 4 int main() 5 { 6 scanf("%d",&n); 7 int x; 8 int now=n; 9 for(int i=1;i<=n;++i) 10 { 11 scanf("%d",&x); 12 vis[x]=1; 13 if(vis[now]) 14 { 15 printf("%d",now); 16 --now; 17 while(vis[now]) 18 { 19 printf(" %d",now); 20 --now; 21 } 22 } 23 printf("\n"); 24 } 25 return 0; 26 }
B题
又是水题,你们肯定要感谢我
只要求每个数对最大的数的绝对值的和就可以了
1 #include<stdio.h> 2 #include<algorithm> 3 using namespace std; 4 int n, ai; 5 int main() 6 { 7 scanf("%d", &n); 8 int maxi = 0; 9 int sum = 0; 10 for (int i = 0; i < n; i++) 11 { 12 scanf("%d", &ai); 13 sum += ai; 14 maxi = max(maxi, ai); 15 } 16 int res = maxi * n - sum; 17 printf("%d", res); 18 return 0; 19 }
C题
这一题开始就有点难了,DFS+回溯
题目大意: 任选一个起点,按照国际象棋马的跳法,不重复的跳完整个棋盘,如果有多种路线则选择字典序最小的路线(路线是点的横纵坐标的集合,注意棋盘的横坐标的用大写字母,纵坐标是数字)
1. 应该看到这个题就可以想到用DFS,当首先要明白这个题的意思是能否只走一遍(不回头不重复)将整个地图走完,而普通的深度优先搜索是一直走,走不通之后沿路返回到某处继续深搜。所以这个题要用到的回溯思想,如果不重复走一遍就走完了,做一个标记,算法停止;否则在某种DFS下走到某一步时按马跳的规则无路可走而棋盘还有为走到的点,这样我们就需要撤消这一步,进而尝试其他的路线(当然其他的路线也可能导致撤销),而所谓撤销这一步就是在递归深搜返回时重置该点,以便在当前路线走一遍行不通换另一种路线时,该点的状态是未访问过的,而不是像普通的DFS当作已经访问了。
2. 如果有多种方式可以不重复走一遍的走完,需要输出按字典序最小的路径,而注意到国际象棋的棋盘是列为字母,行为数字,如果能够不回头走一遍的走完,一定会经过A1点,所以我们应该从A1开始搜索,以确保之后得到的路径字典序是最小的(也就是说如果路径不以A1开始,该路径一定不是字典序最小路径),而且我们应该确保优先选择的方向是字典序最小的方向,这样我们最先得到的路径就是字典序最小的。
1 #include <stdio.h> 2 #include <algorithm> 3 #include<string.h> 4 using namespace std; 5 6 const int MAX_N = 27; 7 8 const int dx[8] = {-1, 1, -2, 2, -2, 2, -1, 1}; 9 const int dy[8] = {-2, -2, -1, -1, 1, 1, 2, 2}; 10 bool visited[MAX_N][MAX_N]; 11 struct Step{ 12 char x, y; 13 } path[MAX_N]; 14 bool success; //是否成功遍历的标记 15 int cases, p, q; 16 17 void DFS(int x, int y, int num); 18 19 int main() 20 { 21 scanf("%d", &cases); 22 for (int c = 1; c <= cases; c++) 23 { 24 success = false; 25 scanf("%d%d", &p, &q); 26 memset(visited, false, sizeof(visited)); 27 visited[1][1] = true; //起点 28 DFS(1, 1, 1); 29 printf("Scenario #%d:\n", c); 30 if (success) 31 { 32 for (int i = 1; i <= p * q; i++) 33 printf("%c%c", path[i].y, path[i].x); 34 printf("\n"); 35 } 36 else 37 printf("impossible\n"); 38 if (c != cases) 39 printf("\n"); //注意该题的换行 40 } 41 return 0; 42 } 43 44 void DFS(int x, int y, int num) 45 { 46 path[num].y = y + ‘A‘ - 1; //int 转为 char 47 path[num].x = x + ‘0‘; 48 if (num == p * q) 49 { 50 success = true; 51 return; 52 } 53 for (int i = 0; i < 8; i++) 54 { 55 int nx = x + dx[i]; 56 int ny = y + dy[i]; 57 if (0 < nx && nx <= p && 0 < ny && ny <= q 58 && !visited[nx][ny] && !success) 59 { 60 visited[nx][ny] = true; 61 DFS(nx, ny, num+1); 62 visited[nx][ny] = false; //撤销该步 63 } 64 } 65 }
D题
这题是我原创的题目233,其实是一道有关分治的裸题,其实就是最接近点对问题
给定平面S上n个点,找其中的一对点,使得在n(n-1)/2 个点对中,该点对的距离最小。
算法思路就是
1) n较小时直接求 (n=2). 2) 将S上的n个点分成大致相等的2个子集S1和S2 3) 分别求S1和S2中的最接近点对 4) 求一点在S1、另一点在S2中的最近点对 5) 从上述三对点中找距离最近的一对.
仔细分析上面的过程,只有第4步有些困难,其它几个步骤只要递归下去就行了。看下面这个图,我们把点分成了左右两部分S1和S2,并且分别求得了最小距离为dl,dr。 令 d = min(dl,dr):
接下来要考虑的就是 两个端点分别在左右两部分中的点对。已经知道最小距离不会超过d。我们在拆分两部分的时候把 P [N/2] 作为中间点。做一条过P的垂直线,并找到所有距离垂直线距离在d以内的点,把这些点放在数组strip[] 中,如下图所示。其它点就可以不用考虑了。
接下来计算strip[]中的最小距离点对。按照y坐标排序,这一步需要O(nLogn)的复杂度,其实可以 通过递归和归并优化到O(n)。由于已经排序,我们可以再O(n)内找到最小距离点对d3(这里其实需要一些几何知识来证明,参考:http://people.csail.mit.edu/indyk/6.838-old/handouts/lec17.pdf)。
贴上学长T^T的代码
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 const double INF = 1e20; 8 const int N = 100005; 9 10 struct Point 11 { 12 double x; 13 double y; 14 }point[N]; 15 int n; 16 int tmpt[N]; 17 18 bool cmpxy(const Point& a, const Point& b) 19 { 20 if(a.x != b.x) 21 return a.x < b.x; 22 return a.y < b.y; 23 } 24 25 bool cmpy(const int& a, const int& b) 26 { 27 return point[a].y < point[b].y; 28 } 29 30 double min(double a, double b) 31 { 32 return a < b ? a : b; 33 } 34 35 double dis(int i, int j) 36 { 37 return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x) 38 + (point[i].y-point[j].y)*(point[i].y-point[j].y)); 39 } 40 41 double Closest_Pair(int left, int right) 42 { 43 double d = INF; 44 if(left==right) 45 return d; 46 if(left + 1 == right) 47 return dis(left, right); 48 int mid = (left+right)>>1; 49 double d1 = Closest_Pair(left,mid); 50 double d2 = Closest_Pair(mid+1,right); 51 d = min(d1,d2); 52 int i,j,k=0; 53 //分离出宽度为d的区间 54 for(i = left; i <= right; i++) 55 { 56 if(fabs(point[mid].x-point[i].x) <= d) 57 tmpt[k++] = i; 58 } 59 sort(tmpt,tmpt+k,cmpy); 60 //线性扫描 61 for(i = 0; i < k; i++) 62 { 63 for(j = i+1; j < k && point[tmpt[j]].y-point[tmpt[i]].y<d; j++) 64 { 65 double d3 = dis(tmpt[i],tmpt[j]); 66 if(d > d3) 67 d = d3; 68 } 69 } 70 return d; 71 } 72 73 74 int main() 75 { 76 while(~scanf("%d",&n)) 77 { for(int i = 0; i < n; i++) 78 scanf("%lf %lf",&point[i].x,&point[i].y); 79 sort(point,point+n,cmpxy); 80 printf("%lf\n",Closest_Pair(0,n-1)); 81 } 82 return 0; 83 }
E题
这题几何题233。
解法如下:不需要正规的三角剖分,用求多边形面积的思想,从一点出发连接多边形的边得到很多三
角形,三角形有向边方向决定有向面积有正有负,相加得到多边形面积的正值或负值。
把两个多边形都分成若干这样的三角形,求每对三角形的交,根据两三角形有向边顺逆时
针关系确定相交面积的正负号,最后两多边形面积和减去相交面积。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<math.h> 5 #include<algorithm> 6 const int maxn = 555; 7 const int maxisn = 10; 8 const double eps = 1e-8; 9 const double pi = acos(-1.0); 10 int dcmp(double x) 11 { 12 if(x > eps) return 1; 13 return x < -eps ? -1 : 0; 14 } 15 inline double min(double a, double b) 16 {return a < b ? a : b;} 17 inline double max(double a, double b) 18 {return a > b ? a : b;} 19 inline double Sqr(double x) 20 {return x * x;} 21 struct Point 22 { 23 double x, y; 24 Point(){x = y = 0;} 25 Point(double a, double b) 26 {x = a, y = b;} 27 inline Point operator-(const Point &b)const 28 {return Point(x - b.x, y - b.y);} 29 inline Point operator+(const Point &b)const 30 {return Point(x + b.x, y + b.y);} 31 inline double dot(const Point &b)const 32 {return x * b.x + y * b.y;} 33 inline double cross(const Point &b, const Point &c)const 34 {return (b.x - x) * (c.y - y) - (c.x - x) * (b.y - y);} 35 }; 36 Point LineCross(const Point &a, const Point &b, const Point &c, const Point &d) 37 { 38 double u = a.cross(b, c), v = b.cross(a, d); 39 return Point((c.x * v + d.x * u) / (u + v), (c.y * v + d.y * u) / (u + v)); 40 } 41 double PolygonArea(Point p[], int n) 42 { 43 if(n < 3) return 0.0; 44 double s = p[0].y * (p[n - 1].x - p[1].x); 45 p[n] = p[0]; 46 for(int i = 1; i < n; ++ i) 47 s += p[i].y * (p[i - 1].x - p[i + 1].x); 48 return fabs(s * 0.5); 49 } 50 double CPIA(Point a[], Point b[], int na, int nb)//ConvexPolygonIntersectArea 51 { 52 Point p[maxisn], tmp[maxisn]; 53 int i, j, tn, sflag, eflag; 54 a[na] = a[0], b[nb] = b[0]; 55 memcpy(p, b, sizeof(Point) * (nb + 1)); 56 for(i = 0; i < na && nb > 2; ++ i) 57 { 58 sflag = dcmp(a[i].cross(a[i + 1], p[0])); 59 for(j = tn = 0; j < nb; ++ j, sflag = eflag) 60 { 61 if(sflag >= 0) tmp[tn ++] = p[j]; 62 eflag = dcmp(a[i].cross(a[i + 1], p[j + 1])); 63 if((sflag ^ eflag) == -2) 64 tmp[tn ++] = LineCross(a[i], a[i + 1], p[j], p[j + 1]); 65 } 66 memcpy(p, tmp, sizeof(Point) * tn); 67 nb = tn, p[nb] = p[0]; 68 } 69 if(nb < 3) return 0.0; 70 return PolygonArea(p, nb); 71 } 72 double SPIA(Point a[], Point b[], int na, int nb)//SimplePolygonIntersectArea 73 { 74 int i, j; 75 Point t1[4], t2[4]; 76 double res = 0, if_clock_t1, if_clock_t2; 77 a[na] = t1[0] = a[0], b[nb] = t2[0] = b[0]; 78 for(i = 2; i < na; ++ i) 79 { 80 t1[1] = a[i - 1], t1[2] = a[i]; 81 if_clock_t1 = dcmp(t1[0].cross(t1[1], t1[2])); 82 if(if_clock_t1 < 0) std::swap(t1[1], t1[2]); 83 for(j = 2; j < nb; ++ j) 84 { 85 t2[1] = b[j - 1], t2[2] = b[j]; 86 if_clock_t2 = dcmp(t2[0].cross(t2[1], t2[2])); 87 if(if_clock_t2 < 0) std::swap(t2[1], t2[2]); 88 res += CPIA(t1, t2, 3, 3) * if_clock_t1 * if_clock_t2; 89 } 90 } 91 return PolygonArea(a, na) + PolygonArea(b, nb) - res; 92 } 93 Point p1[maxn], p2[maxn]; 94 int n1, n2; 95 int main() 96 { 97 int i; 98 while(scanf("%d%d", &n1, &n2) != EOF) 99 { 100 for(i = 0; i < n1; ++ i) scanf("%lf%lf", &p1[i].x, &p1[i].y); 101 for(i = 0; i < n2; ++ i) scanf("%lf%lf", &p2[i].x, &p2[i].y); 102 printf("%.2f\n", SPIA(p1, p2, n1, n2) + eps); 103 } 104 return 0; 105 }
F题
这题怎么说呢,题意就是:给一系列操作,每个操作有两个数t和k,t=0表示求k以内的最大反素数;t=1表示求小于k且与k互质的数的个数。
大概就是对第一个操作,直接用dfs求反素数就行了;
第二个操作素数筛法的思想预先打个表。
1 #include<stdio.h> 2 #include<algorithm> 3 const long long INF = (((long long)1)<<62)+1; 4 using namespace std; 5 const int MAXN = 300 + 10; 6 const double M=1e-8; 7 const int N=50100; 8 const int d[4][2]={{0,1},{0,-1},{-1,0},{1,0}}; 9 int n,m,st[N],lazy[N],a[N]; 10 int p[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; 11 long long ans,ip[N]; 12 void init() 13 { 14 int i,j; 15 for(i=1;i<N;i++) ip[i]=i; //初始化i最多有i个与其互质的数 16 for(i=1;i<N;i++) 17 { 18 for(j=i;j<=N;j+=i) ip[j]--; //i为j的因子,所以j与i不互质,第j个数的互质数减少1 19 if(!ip[ip[i]]) ip[ip[i]]=i; //此时ip[i]存的是不大于i的数与i互质的个数k=ip[i], 20 //如果ip[k]=0,表示小于i的所有数中,没有刚好有k个互质数的数 21 //故将ip[k]=i,表示刚好有k个与i互质的数个数最小为i 22 ip[i]=0; //标记刚好有k个互质数的数没有(不大于i的数不可能存在某个数与其互质的数的个数等于i); 23 } 24 } 25 void dfs(int k,long long num,long long sum,int limit) 26 { 27 if (sum>n) return ; 28 if (sum==n) ans=min(ans,num); 29 for (int i=1;i<=limit;i++) { 30 if (ans/p[k] <num || sum*(i+1)>n) break; 31 num*=p[k]; 32 if (n%(sum*(i+1))==0) 33 dfs(k+1,num,sum*(i+1),i); 34 } 35 } 36 int main() 37 { 38 int i,j,cnt=1; 39 int t; 40 init(); 41 scanf("%d",&t); 42 while(t--) 43 { 44 int t; 45 scanf("%d%d",&t,&n); 46 if (t) { 47 ans=ip[n]; 48 } 49 else { 50 ans=INF; 51 dfs(0,1,1,62); 52 } 53 printf("Case %d: ",cnt++); 54 if (ans==0) puts("Illegal"); 55 else if (ans>=INF) puts("INF"); 56 else printf("%lld\n",ans); 57 } 58 }
第六周周赛题解