好久没做手生了,不然前四道都是能A的,当然,正常发挥也是菜。
A:Launch of Collider
题意:20万个点排在一条直线上,其坐标均为偶数。从某一时刻开始向左或向右运动,速度为每秒1个单位长度。输入给出每个点的坐标及其移动的方向,求发生第一次碰撞的时间,若不会碰撞,则输出-1
最先发生碰撞的是一定是初始时相邻的两个点,因此只需对每个点循环一边,判断其是否会与下一个点碰撞,并求出其时间即可。
#include<stdio.h> #include<stdlib.h> int N; int pos[200100]; char ch[200100]; char st[200100]; int main() { scanf ( "%d", &N ); getchar(); for ( int i = 1; i <= N; i++ ) scanf ( "%c", &ch[i] ); for ( int i = 1; i <= N; i++ ) scanf ( "%d", &pos[i] ); int ans = 0x7FFFFFFF, p = 0; for ( int i = 1; i <= N; i++ ) { if ( ch[i] == 'R' ) { p = i; continue; } if ( p == 0 ) continue; if ( ( pos[i] - pos[p] ) / 2 < ans ) ans = ( pos[i] - pos[p] ) / 2; } if ( ans == 0x7FFFFFFF ) printf ( "-1 " ); else printf ( "%d ", ans ); return 0; }
B:One Bomb
题意:1000*1000的矩阵,‘.’表示空地,‘*’表示围墙。可以在任意位置按一颗炸弹,摧毁位于同一行和同一列的所有墙。问对于给定的地图,能否只用一颗炸弹炸掉所有墙。
首先随意指定一个墙点,将其坐标与其他所有墙点比较,如果其他墙点与这个点要么在同一行,要么在同一列,则可以完成,否则能找到一个与第一个点既不在同一行也不在同一列的墙点。根据这两个点可以确定两个位置,炸弹如果要摧毁所有的墙,只能在这两个点中选择一个,分别尝试并确认即可。
#include<stdio.h> int p[1001000][2]; char str[1005][1005]; int main() { int N, M; scanf ( "%d%d", &N, &M ); for ( int i = 1; i <= N; i++ ) scanf ( "%s", &str[i][1] ); int T = 0; for ( int i = 1; i <= N; i++ ) for ( int j = 1; j <= M; j++ ) if ( str[i][j] == '*' ) { p[++T][0] = i; p[T][1] = j; } if ( T == 0 ) { printf ( "YES 1 1 " ); return 0; } bool find = false; int x, y; for ( int i = 2; i <= T; i++ ) if ( p[i][0] != p[1][0] && p[i][1] != p[1][1] ) { find = true; x = p[i][0]; y = p[i][1]; break; } if ( !find ) { printf ( "YES " ); printf ( "%d %d ", p[1][0], p[1][1] ); return 0; } else { bool planA = true, planB = true; int x1 = x, y1 = p[1][1]; for ( int i = 1; i <= T; i++ ) if ( p[i][0] != x1 && p[i][1] != y1 ) { planA = false; break; } int x2 = p[1][0], y2 = y; for ( int i = 1; i <= T; i++ ) if ( p[i][0] != x2 && p[i][1] != y2 ) { planB = false; break; } if ( !planA && !planB ) { printf ( "NO " ); return 0; } printf ( "YES " ); printf ( "%d %d ", planA ? x1 : x2, planA ? y1 : y2 ); } return 0; }
C:Vacations
题意:每天可以有3种选择,发呆,锻炼身体或者打比赛,连着两天如果不发呆则做的事不能相同。求发呆的最少天数。
f[i][0]表示到第i天位置且当天发呆的最小发呆天数,f[i][1]表示当天锻炼身体,f[i][2]表示当天打比赛。
#include<stdio.h> int MIN2 ( int a, int b ) { return a < b ? a : b; } int MIN3 ( int a, int b, int c ) { int t = 10000; if ( t > a ) t = a; if ( t > b ) t = b; if ( t > c ) t = c; return t; } int f[200][5]; int d[200]; int main() { int N; scanf ( "%d", &N ); for ( int i = 1; i <= N; i++ ) scanf ( "%d", &d[i] ); f[0][0] = 0; f[0][1] = 1000; f[0][2] = 1000; for ( int i = 1; i <= N; i++ ) { if ( d[i] == 0 ) { f[i][0] = MIN3 ( f[i - 1][0], f[i - 1][1], f[i - 1][2] ) + 1; f[i][1] = 1000; f[i][2] = 1000; } if ( d[i] == 1 ) { f[i][0] = MIN3 ( f[i - 1][0], f[i - 1][1], f[i - 1][2] ) + 1; f[i][1] = MIN2 ( f[i - 1][0], f[i - 1][2] ); f[i][2] = 1000; } if ( d[i] == 2 ) { f[i][0] = MIN3 ( f[i - 1][0], f[i - 1][1], f[i - 1][2] ) + 1; f[i][1] = 1000; f[i][2] = MIN2 ( f[i - 1][0], f[i - 1][1] ); } if ( d[i] == 3 ) { f[i][0] = MIN3 ( f[i - 1][0], f[i - 1][1], f[i - 1][2] ) + 1; f[i][1] = MIN2 ( f[i - 1][0], f[i - 1][2] ); f[i][2] = MIN2 ( f[i - 1][0], f[i - 1][1] ); } } printf ( "%d ", MIN3 ( f[N][0], f[N][1], f[N][2] ) ); return 0; } /* 8 1 1 1 1 1 1 1 2 0 0 1 1 2 2 3 3 4 4 1 x 0 1 1 2 2 3 3 x 2 x x x x x x x x 3 1->0,1 2->0,2 3->0,1,2 */
D:Fix a Tree
题意:给定n个点各自的父亲,得到一个n个点n条边的有向图。每次可以修改一个点的父亲,求最少修改多少次才能得到一棵树(树的根节点的父亲是其自身)。
首先在给出的点里找到一个父亲是自身的点作为根节点,然后从图中搜索环,并把环上任意一点的父亲改为根节点。若找不到父亲为自身的点,则任意找到图中的一个环,从环上断开,并将断点的父亲修改为自身,作为根节点,并重复之前的步骤。
#include<stdio.h> #include<string.h> #include<stdlib.h> int fa[210000]; bool v[210000], u[210000]; int main() { int N, Root = -1, cnt; scanf ( "%d", &N ); for ( int i = 1; i <= N; i++ ) scanf ( "%d", &fa[i] ); memset ( v, false, sizeof ( v ) ); for ( int i = 1; i <= N; i++ ) if ( fa[i] == i ) { Root = i; v[i] = true; break; } if ( Root != -1 ) { cnt = 0; for ( int i = 1; i <= N; i++ ) if ( !v[i] ) { memset ( u, false, sizeof ( u ) ); int x = i; v[x] = true; u[x] = true; while ( !v[fa[x]] ) { v[fa[x]] = true; u[fa[x]] = true; x = fa[x]; } if ( u[fa[x]] ) { cnt++; fa[x] = Root; } } } else { Root = 1; v[1] = true; while ( !v[fa[Root]] ) { v[fa[Root]] = true; Root = fa[Root]; } cnt = 1; fa[Root] = Root; for ( int i = 1; i <= N; i++ ) if ( !v[i] ) { memset ( u, false, sizeof ( u ) ); int x = i; v[x] = true; u[x] = true; while ( !v[fa[x]] ) { v[fa[x]] = true; u[fa[x]] = true; x = fa[x]; } if ( u[fa[x]] ) { cnt++; fa[x] = Root; } } } printf ( "%d ", cnt ); for ( int i = 1; i < N; i++ ) printf ( "%d ", fa[i] ); printf ( "%d ", fa[N] ); return 0; }
E:LRU
题意:自己读吧,懒得写了。
题目里问1e100次后的概率,显然不用真的算到1e100,它只是表示一个“足够多”的概念。由于每种目标或者有或者没有只有两种状态,而目标的数目最多20个,因此可以用一个20位的二进制数表示所有可能的状态。经过足够多的次数后,槽位全部被填满,此后的状态转移并不影响概率。因此对于每一种状态,只需计算刚刚好达到这种状态的概率即可,也就是说,不用考虑弹出之前的某个目标的情况。
#include<stdio.h> int N, K; double p[22]; double ans[22]; double dp[1 << 20 + 100]; int main() { int k; double sp; scanf ( "%d%d", &N, &K ); for ( int i = 0; i < N; i++ ) { scanf ( "%lf", &p[i] ); } dp[0] = 1; for ( int i = 1; i < ( 1 << N ); i++ ) { sp = 0; k = 0; for ( int j = 0; j < N; j++ ) { if ( ( ( 1 << j ) &i ) == 0 ) { sp += p[j]; k++; } } if ( N - k > K ) continue; for ( int j = 0; j < N; j++ ) { if ( p[j] < 0.000000001 ) continue; if ( ( ( 1 << j ) &i ) != 0 ) { dp[i] += dp[i - ( 1 << j )] * p[j] / ( sp + p[j] ); ans[j] += dp[i - ( 1 << j )] * p[j] / ( sp + p[j] ); } } } for ( int i = 0; i < N; i++ ) { printf ( "%.10lf ", ans[i] ); } return 0; }