Noip 2015 senior复赛 题解
今天我们再次完成了这套曾今让我非常神清气爽(丧心病狂)的题目。
记得上一次做这套题目的时候,我还处在人生的顶峰,那时作为一名非常有姿势的蒟蒻,我的模拟能力那是相当强啊,当时大家都还只是学习了基本语法和算法,连图论是什么都不知道,所以我很自信的拿了一次相对的高分(其实相当低)。
今天再次看到这套题,非常亲切啊(才怪哩)。
首先是T1
神奇的幻方(magic),这道题对当时的我来说是相当神奇的,居然我们在平时做出一个类似数独的矩形可以这样的开挂。当然,这道题就是一个while加上多个if不断的判断就好了。我觉得,顶多,你把中间改变方向的判断用switch。
然后我们进入T2
信息的传递(message),这道题我记得我当时就是直接模拟的,后来……就TLE了啊。我的印象中老师是讲过的,但我果断还是忘了。时至今日,我细细思来其实也没什么好难嘛。
这道题的意思是说这里有一群人在玩游戏(而我在学习),他们非常无聊的决定不断的传递给其他人自己知晓的生日。然而这些人又非常不会交际,一起耍了这么久居然都只知道自己的生日,所以他们初始都只知道自己的生日。在传递的时候他们如果被一个人告知了自己的生日,他们就觉得很没有面子(自己的交际圈和交际圈里人的交际圈实在是太小了),所以就算做游戏结束了。而交给我们的任务就是让我们找到结束这个游戏的最短时间(次数)。
哇,我今天又说了很多。仔细分析我刚刚所说的故事,你就会发现这道题的最终目的就是让我去寻找一个最小环!其余全是废话。那么找最小环的方法那么多,我们选择哪一种呢?我当然选择直接找的那种(最好想)。对于一个环来说,形成环的条件是什么呢?当然是自己的头部碰到了自己的尾部啊。所以我们所做的工作也就是找每一个环的头部碰到尾部所需要的次数,我们输出最小次数。我们可以用一个数组记录每个点的最小环长度,然后如果我们在搜索的过程中找到了一个点,那么我们可以更新到达此点所需次数,对吧?同时我们将搜索到的每个点都为其保存一个根结点数组(即所属环的头部在哪里)。然后将该点继续向下移动,如果移动后的点的根结点数组里对应的根结点已经有所保存,则说明此时有一个环的形成。这时我们进行一个判断,如果这个点的根结点是我们进入时那个点的话,证明我们此时找了一个新的环,至于是不是最小,我们将其和答案判断一下择优,就好了(如果该点的根结点不是我们进入时的点,证明不是一个较小的环)。
当然,我们同样也可以使用floyd算法来求解。Floyd的求解是建立在我们能够使用Dijkstra求解的基础上的。
任意一个环的权值,我们都可以看成两个有边相连的结点i、j的直接距离加上i、j间不包含边(边i->j)的最短路径。求最短路径我们第一个想到的就是Dijkstra算法。而Dijkstra所求的是一个点到所有点的最短距离。用Dijkstra所求的i、j的最短距离一定是i、j的直接距离(如果i,j连通),所以我们需要先将i、j的边从图中删除(若i,j不连通,则不用删除),再用Dijkstra求新图中i、j的最短距离即可。所以我们每次在图中选取一条边,把它从图中删掉.然后对删掉的那条边所对应的2点进行Dijkstra,也就是m次Dijkstra。Dijkstra是存在最坏情况的,O(n^4)。
那么,对于floyd算法,我们求解时间复杂度就要稳定的多,O(n^3)。
至于T3的话我们直接搜索加贪心就好了,由于顺子比较舒服嘛,我们先出顺子,然后处理带牌,最后再处理对子和单张就好了。用一个DFS来处理。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 #define N 45 6 7 int maps[N][N]; 8 int n,idx; 9 int i = 1,j,flag; 10 11 int main() 12 { 13 freopen("magic.in","r",stdin); 14 freopen("magic.out","w",stdout); 15 16 scanf("%d",&n); 17 j = n / 2 + 1; 18 while(idx < n * n) 19 { 20 flag = 0; 21 maps[i][j] = ++idx; 22 if(i == 1 && j != n) 23 { 24 flag = 1; 25 i = n; 26 j++; 27 } 28 if(j == n && i != 1 && flag == 0) 29 { 30 flag = 2; 31 j = 1; 32 i--; 33 } 34 if(i == 1 && j == n && flag == 0) 35 { 36 flag = 3; 37 j = 1; 38 i = n; 39 } 40 if(flag == 0) 41 { 42 i--; 43 j++; 44 } 45 if(maps[i][j]) 46 { 47 if(flag == 0) 48 { 49 i += 2; 50 j--; 51 } 52 if(flag == 1) 53 { 54 i = 1; 55 } 56 if(flag == 2) 57 { 58 i++; 59 } 60 if(flag == 3) 61 { 62 i = 2; 63 j = n; 64 } 65 } 66 } 67 for(int i=1;i<n;i++) 68 { 69 for(int j=1;j<n;j++) 70 { 71 printf("%d ",maps[i][j]); 72 } 73 printf("%d",maps[i][n]); 74 putchar('\n'); 75 } 76 for(int i=1;i<n;i++) printf("%d ",maps[n][i]); 77 printf("%d",maps[n][n]); 78 return 0; 79 }
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 #define N 200010 6 7 int use[N],times[N]; 8 int n,t[N],ans = N + 10000; 9 10 inline int read(){ 11 int data=0,w=1; 12 char ch=0; 13 while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar(); 14 if(ch=='-') w=-1,ch=getchar(); 15 while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar(); 16 return data*w; 17 } 18 19 inline void write(int x) 20 { 21 if(x<0) putchar('-'),x=-x; 22 if(x>9) write(x/10); 23 putchar(x%10+'0'); 24 } 25 26 void dfs(int x) 27 { 28 int k = x,cnt = 0; 29 while(true) 30 { 31 times[x] = cnt++; 32 use[x] = k; 33 x = t[x]; 34 if(use[x] > 0) 35 { 36 if(use[x] == k) 37 { 38 ans = min(ans,cnt - times[x]); 39 break; 40 } 41 else 42 { 43 ans = ans; 44 break; 45 } 46 } 47 } 48 } 49 50 int main() 51 { 52 // freopen("message.in","r",stdin); 53 // freopen("message.out","w",stdout); 54 55 n = read(); 56 for(int i=1;i<=n;i++) 57 { 58 t[i] = read(); 59 } 60 for(int i=1;i<=n;i++) 61 { 62 if(use[i] == 0) 63 { 64 dfs(i); 65 } 66 } 67 write(ans); 68 69 return 0; 70 }