【前言】由于并不是所有的同学都能进决赛,没有进决赛 (等待通知名单) 的同学也不要气馁,后面还会有很多机会。只要你坚持、谦虚、低调、勤奋,相信你一定会成为一名出色的编码选手。
【A题】略。
【B题】
基本思想:模拟每一步的处理,对于每一步重在实现上、下、左、右四种情况下的处理。
#include<stdio.h> #include<string.h> const int maxN = 5; int maze[maxN][maxN]; char cmd[maxN * 100]; // L, R, U, D int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; void go(int d) { // L if( d == 0 ) { for(int i = 0; i < 4; i ++) { for(int j = 0; j + 1 < 4; ) { if( maze[i][j] == 0 ) { j ++; continue; } int k = j + 1; for(; k < 4; k ++) if( maze[i][k] ) break; if( k < 4 && maze[i][j] == maze[i][k] ) { // 处理相同 maze[i][j] <<= 1; maze[i][k] = 0; j = k + 1; } else j ++; } int k = 0; for(int j = 0; j < 4; j ++) // 向左靠齐 if( maze[i][j] ) maze[i][k ++] = maze[i][j]; while( k < 4 ) maze[i][k ++] = 0; } } // R if( d == 1 ) { for(int i = 0; i < 4; i ++) { for(int j = 3; j - 1 >= 0; ) { if( maze[i][j] == 0 ) { j --; continue; } int k = j - 1; for(; k >= 0; k --) if( maze[i][k] ) break; if( k >= 0 && maze[i][j] == maze[i][k] ) { maze[i][j] <<= 1; maze[i][k] = 0; j = k - 1; } else j --; } int k = 3; for(int j = 3; j >= 0; j --) if( maze[i][j] ) maze[i][k --] = maze[i][j]; while( k >= 0 ) maze[i][k --] = 0; } } // U if( d == 2 ) { for(int j = 0; j < 4; j ++) { for(int i = 0; i + 1 < 4; ) { if( maze[i][j] == 0 ) { i ++; continue; } int k = i + 1; for(; k < 4; k ++) if( maze[k][j] ) break; if( k < 4 && maze[i][j] == maze[k][j] ) { maze[i][j] <<= 1; maze[k][j] = 0; i = k + 1; } else i ++; } int k = 0; for(int i = 0; i < 4; i ++) if( maze[i][j] ) maze[k ++][j] = maze[i][j]; while( k < 4 ) maze[k ++][j] = 0; } } // D if( d == 3 ) { for(int j = 0; j < 4; j ++) { for(int i = 3; i - 1 >= 0; ) { if( maze[i][j] == 0 ) { i --; continue; } int k = i - 1; for(; k >= 0; k --) if( maze[k][j] ) break; if( k >= 0 && maze[i][j] == maze[k][j] ) { maze[i][j] <<= 1; maze[k][j] = 0; i = k - 1; } else i --; } int k = 3; for(int i = 3; i >= 0; i --) if( maze[i][j] ) maze[k --][j] = maze[i][j]; while( k >= 0 ) maze[k --][j] = 0; } } } int main() { int t = 0; while( scanf("%d", &maze[0][0]) == 1 ) { if( t ) puts(""); else t ++; for(int j = 1; j < 4; j ++) scanf("%d", &maze[0][j]); for(int i = 1; i < 4; i ++) for(int j = 0; j < 4; j ++) scanf("%d", &maze[i][j]); scanf("%s", cmd); for(int i = 0; cmd[i]; i ++) { if( cmd[i] == 'L' ) go(0); if( cmd[i] == 'R' ) go(1); if( cmd[i] == 'U' ) go(2); if( cmd[i] == 'D' ) go(3); } for(int i = 0; i < 4; i ++) { for(int j = 0; j < 4; j ++) printf("%5d", maze[i][j]); printf("\n"); } } return 0; }
while( scanf("%s", str) == 1 ) { memset(times, 0, sizeof(times)); for(int i = 0; str[i]; i ++) times[ str[i] ] ++; int res = times['g'] - 1; // 可共用 res = min(res, times['u']); res = min(res, times['a']); res = min(res, times['n'] >> 1); res = min(res, times['j']); res = min(res, times['i']); printf("%d\n", max(0, res)); }
import java.io.*; import java.math.*; import java.util.*; public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Scanner sys = new Scanner(System.in); int N, M, K, V, ones, twos, res, a, b, c; while( sys.hasNext() ) { N = sys.nextInt(); M = sys.nextInt(); K = sys.nextInt(); ones = 0; twos = 0; for(int i = 0; i < N; i ++) { V = sys.nextInt(); if( V == 1 ) ones ++; else twos ++; } res = 1000000009; for(int i = 0; i <= twos; i ++) { // 拆分第二种选择 a = ones + i; b = twos - i; c = 0; if( a > M ) c += a - M; if( b > K ) c += b - K; if( c < res ) res = c; } if( res < 0 ) res = 0; System.out.println(res); } System.gc(); } }
动态规划题,相信你可以推导出如下表达式:
(表达式的含义,不妨留作思考题)
import java.io.*; import java.math.*; import java.util.*; public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int Mod = 20120354; int[][] dp = new int [1502][1502]; for(int j = 1; j <= 1500; j ++) dp[1][j] = 1; for(int i = 2; i <= 1500; i ++) { for(int j = 1; j <= 1500; j ++) { dp[i][j] = 0; for(int d = 1; d * d <= j; d ++) { if( j % d == 0 ) { dp[i][j] = (dp[i][j] + dp[i - 1][d]) % Mod; if( d * d != j ) dp[i][j] = (dp[i][j] + dp[i - 1][j / d]) % Mod; } } } } // 预处理所有临时结果 Scanner sys = new Scanner(System.in); while( sys.hasNext() ) { int N = sys.nextInt(); int K = sys.nextInt(); int sum = 0; for(int j = 1; j <= K; j ++) // 最后一个数不大于K sum = (sum + dp[N][j]) % Mod; System.out.println(sum); } //System.gc(); } }
不妨假设A的左边是B,右边是C,很容易想到,最优方案中,A要么不参与传递,要么只拿出一部分给B,要么只拿出一部分给C,要么同时分别拿出一部分给B和C。看上去很复杂?假设总数是S,显然如果S不能被人数除尽,则肯定是不能平均分配,这一点显而易见;如果能够除尽,则必然可以平均分配,进而此时考虑的问题就是最优的问题!
假设最优方案中,是A拿出x个给B,拿出y个给C,注意:x和y都可以为0,比如如果x=0,表示A实际上并没有拿给B。可见,用这样的表达方式可以把所有情况都能表示出来!而为了解决这道题,我们需要转变一个认识,那就是你可能会觉得即便x和y都可能等于0,但是也必然是非负的,不是吗?而我想说的是,x和y可以为任意整数,包括负数!考虑一下,假设x=-3,原汁原味的意思就是A拿出-3个给B,而这是不是等价于B拿出3个给A呢?好了,有了这样的认识,解决这道题不再困难!【我的初衷总是让你尽可能明白解题的思路,而不在于代码的实现!】
好了,我们用<P, Q, n>表示P拿出n个给予Q,注意n为任意整数。不失一般性,假设现在由N个人,即P(1), P(2), P(3), ..., P(N),他们手中已有的糖果数分别是:T(1), T(2), ..., T(N)。那么,在最优方案中,首先一点可以确定,他们手中的糖果肯定会优先考虑给自己,然后如果有多余的,才给别人!既然如此,也就是说,只有多余的糖果才会被传来传去,那么每个人手中有多少是多余的呢?假设糖果平均数是V个,显然第k个人有T(k)-V个多余的!注意,这个值也可能为负数,一样的道理,假设它的值为-7,原汁原味的意思就是多出了-7个,实际上是少了7个,这是等价的,我们只从多出的角度思考问题。【很多时候我们要转变思维认识,不要定格在现实的牢套当中!】
我们要推导出一个结论:任意两相邻的人之间至多有一次传输!为什么呢?假设现在有相邻的两个人A和B,某方案中,A拿出x个给B,B拿出y个给A,也即:<A, B, x>和<B, A, y>,而这个显然可以用一个来表示即:<A, B, x - y>。所以可以看到,基于这一点,我们可以得出,最优方案总可以写成下面的形式:
<P(1), P(2), n(1)>, <P(2), P(3), n(2)>, ..., <P(N), P(1), n(N)>
可以看到,这不就说明最优方案总是可以转化为朝着1个方向走的吗?如果朝着1个方向走,从哪个人开始呢?大不了枚举一下?那又怎么计算答案呢?(点到为止,留作思考题)
#include<stdio.h> #include<string.h> const int maxN = 1024; long long A[maxN]; int nA; int main() { while( scanf("%d", &nA) == 1 ) { for(int i = 0; i < nA; i ++) scanf("%lld", &A[i]); long long sum = 0; for(int i = 0; i < nA; i ++) sum += A[i]; if( sum % nA ) { puts("oh,no!"); continue; } sum /= nA; // 计算平均数 for(int i = 0; i < nA; i ++) A[i] -= sum; // 每一个值减去平均数 long long res = -1; for(int i = 0; i < nA; i ++) { // 正方向枚举起始点 long long ret = 0, tmp = A[i]; for(int j = (i + 1) % nA; j != i; j = (j + 1) % nA) { ret += tmp > 0 ? tmp : (-tmp); tmp = A[j] + tmp; } // 留作思考 if( res == -1 || ret < res ) res = ret; // 取较小值 } long long t; for(int i = 0, j = nA - 1; i < j; i ++, j --) t = A[i], A[i] = A[j], A[j] = t; // 逆序 for(int i = 0; i < nA; i ++) { // 反方向枚举起始点 long long ret = 0, tmp = A[i]; for(int j = (i + 1) % nA; j != i; j = (j + 1) % nA) { ret += tmp > 0 ? tmp : (-tmp); tmp = A[j] + tmp; } // 留作思考 if( res == -1 || ret < res ) res = ret; // 取较小值 } printf("%lld\n", res); } return 0; }
【H题】略。
#include<stdio.h> #include<string.h> const int maxN = 64; char A[maxN], B[maxN], C[maxN]; double get(char str[]) { int dot_pos = -1; for(int i = 0; str[i]; i ++) if( str[i] == '.' ) { dot_pos = i; break; } double res = 0.0; if( dot_pos == -1 ) { sscanf(str, "%lf", &res); strcpy(str, ""); return res; } str[dot_pos] = ' '; sscanf(str, "%lf %s", &res, C); strcpy(str, C); return res; } // 计算整数部分 long long gcd(long long a, long long b) { if( a == 0LL && b == 0LL ) return 1LL; if( b == 0 ) return a; return gcd(b, a % b); } // 计算最大公约数 void doit(long long &X, long long &Y, char str[]) { int len = strlen(str); X = 0LL; Y = 1LL; if( len == 0 ) return ; int bra_pos = len; for(int i = 0; str[i]; i ++) if( str[i] == '(' ) { bra_pos = i; break; } if( bra_pos == 0 ) return ; for(int i = 0; i < bra_pos; i ++) Y *= 10LL; for(int i = 0; i < bra_pos; i ++) X = 10LL * X + (str[i] - '0'); long long d = gcd(X, Y); X /= d; Y /= d; } // 计算小数部分不包含括号里面的部分 void go(long long &X, long long &Y, char str[]) { int len = strlen(str); X = 0LL; Y = 1LL; if( len == 0 ) return ; int fa = -1, fb = -1; for(int i = 0; str[i]; i ++) if( str[i] == '(' ) fa = i; else if( str[i] == ')' ) fb = i; if( fa == -1 ) return ; long long pre_based = 1LL; for(int i = 0; i < fa; i ++) pre_based *= 10LL; long long cur_based = 1LL, cur_val = 0LL; for(int i = fa + 1; i < fb; i ++) { cur_based *= 10LL; cur_val = 10LL * cur_val + (str[i] - '0'); } long long d1 = gcd(cur_val, pre_based); long long d2 = gcd(cur_val, cur_based - 1LL); X = cur_val / d1 / d2; Y = (pre_based / d1) * ((cur_based - 1LL) / d2); } // 计算小数部分的括号里面的部分 // (a / b) + (c / d) void add(long long a, long long b, long long c, long long d, long long &X, long long &Y) { Y = b * d; X = a * d + b * c; long long dd = gcd(X, Y); X /= dd; Y /= dd; } // 计算两个分数的和 int main() { int nt; scanf("%d", &nt); while( (nt --) > 0 ) { scanf("%s %s", A, B); double int_part_A = get(A); double int_part_B = get(B); long long int_part = (long long)(int_part_A + int_part_B + 0.5); // 先把整数部分加起来 //printf("int_part = %lld\n", int_part); long long pre_bra_A_0, pre_bra_A_1; doit(pre_bra_A_0, pre_bra_A_1, A); long long pre_bra_B_0, pre_bra_B_1; doit(pre_bra_B_0, pre_bra_B_1, B); long long X1, Y1; add(pre_bra_A_0, pre_bra_A_1, pre_bra_B_0, pre_bra_B_1, X1, Y1); // 再把不包括括号部分的小数加起来 //printf("X1/Y1 = %lld/%lld\n", X1, Y1); long long ins_bra_A_0, ins_bra_A_1; go(ins_bra_A_0, ins_bra_A_1, A); long long ins_bra_B_0, ins_bra_B_1; go(ins_bra_B_0, ins_bra_B_1, B); long long X2, Y2; add(ins_bra_A_0, ins_bra_A_1, ins_bra_B_0, ins_bra_B_1, X2, Y2); // 最后把括号里面的部分加起来 //printf("X2/Y2 = %lld/%lld\n", X2, Y2); long long resX, resY; add(X1, Y1, X2, Y2, resX, resY); add(int_part, 1LL, resX, resY, resX, resY); // 求和 printf("%lld/%lld\n", resX, resY); } return 0; }
【I题】略。
能力有限,难免有错,如有误,敬请留言,有其他疑问,也可联系 [email protected]。