题目一:Factorials
题目大意:求n!的最后非0位。
算法:模拟
1.2的因子的个数显然比5多很多;
2.可以预先根据5的个数估计出末尾0的个数,然后只需保存多余2的个数,去更新末位的值;
3.实际做的时候没有想到上述方法,而是鬼使神差地想到保存末5位,但是无法证明!
#include <stdio.h> #include <string.h> unsigned int s[32][32]; void dfs(unsigned int m, int leftone, int dep) { //printf("%d %d %d/n", m, leftone, dep); if (dep == 0) { printf("/n"); return; } if (m <= s[dep-1][leftone]) { printf("0"); dfs(m, leftone, dep-1); } else { printf("1"); dfs(m-s[dep-1][leftone], leftone-1, dep-1); } } int main(){ freopen("kimbits.in", "r", stdin); freopen("kimbits.out", "w", stdout); int n, k, m, i, j; scanf("%d %d %u", &n, &k, &m);//%u used for reading unsigned 10-based numbers memset(s,0,sizeof(s)); for (j = 0; j <= k; j++) s[0][j] = 1; for (i = 1; i <= n; i++) { s[i][0] = 1; for (j = 1; j <= k; j++) s[i][j] = s[i-1][j-1] + s[i-1][j]; } dfs(m, k, n); return 0; }
题目二:Stringsobits
题目大意:在所有长度为N的1的位数不超过K的二进制数中(包含前导0),求出第I大的数
算法:DP
状态转移方程:S[I][J]表示长度为I用1的个数不大于J的二进制数的个数;
S[I][J] = S[I-1][J]+S[I-1][J-1],很像C(A,B)的递推公式啊!
初始条件:s[0][i] = 1,1 <= i <= K
还有一点要注意的是2^31 超过LONGINT的表示范围,可以用UNSIGHED LONGINT,读入格式scanf("%u", &a)
#include <stdio.h> #include <string.h> int main(){ freopen("fact4.in", "r", stdin); freopen("fact4.out", "w", stdout); int n; scanf("%d", &n); int s = 1; for (int i = 1; i <= n; i++) if (i % 5 != 0) s = (s*i) % 100000; else { int j = i; while (j % 5 == 0) s >>= 1, j /= 5; s = (s*j) % 100000; } printf("%d/n", s); return 0; }
题目三:Spinning Wheels
题目大意:有若干个有缺口的转动着的轮子,转的过程是离散的,以时间1为基本单位,求出什么时候在某处光线可以通过。
一种思路是状态仍是离散保存,只保存起始点和末端点,用时间更新初始坐标,判断的时候考虑到有些段可能跨越360的临界,就要分别判断是在360内还是360外。
特别注意:只要有一点缝隙,光就可以过!
#include <stdio.h> #include <string.h> int speed[5], t[5]; int a[5][5], b[5][5]; int main(){ freopen("spin.in", "r", stdin); freopen("spin.out", "w", stdout); int i, j, k; for (i = 0; i < 5; i++) { scanf("%d %d", &speed[i], &t[i]); for (j = 0; j < t[i]; j++) { scanf("%d %d", &a[i][j], &b[i][j]); b[i][j] += a[i][j]; b[i][j] %= 360; } } int tt = 0; bool find = false; do { //CHECK for (i = 0; i < 360 && !find; i++) { bool go = true; for (j = 0; j < 5; j++) { bool flag = false;//if any wedge can through it then flag = true for (k = 0; k < t[j] && !flag; k++) { if (b[j][k] < a[j][k]) b[j][k] += 360; if (a[j][k] <= i && i <= b[j][k] || a[j][k] <= i+360 && i+360 <= b[j][k]) flag = true; //the light between also go through [1,2],[2,3],two ways it go through b[j][k] %= 360; } if (!flag) { go = false; break; } } if (go) { find = true; break; } } if (find) break; //Rotate tt++; for (i = 0; i < 5; i++) for (j = 0; j < t[i]; j++) { a[i][j] = (a[i][j] + speed[i]) % 360; b[i][j] = (b[i][j] + speed[i]) % 360; } } while(tt <= 360); if (find) printf("%d/n", tt); else printf("none/n"); return 0; }
还有一种思路是用BOOL来保存状态,效率相对较低;
题目四:Feed Ratios
1.由于数据比较小,可以BRUTE FORCE,关键在于3点共线判断的方法,不推荐用斜率的方法去做,最好用向量的方法去做;
#include <stdio.h> #include <string.h> int s[4][4], get[4]; bool check(int i, int j) { if (s[0][i]*get[j]-s[0][j]*get[i] == 0) return true; else return false; } int main(){ freopen("ratios.in", "r", stdin); freopen("ratios.out", "w", stdout); for (int i = 0; i <= 3; i++) for (int j = 1; j <= 3; j++) scanf("%d", &s[i][j]); int tot = 300, totx, toty, totz, t, tt; for (int i = 0; i < 100 && i < tot; i++) for (int j = 0; j < 100 && i+j < tot; j++) for (int k = 0; k < 100 && i+j+k < tot && i+j+k>0; k++) { for (int l = 1; l <= 3; l++) get[l] = i*s[1][l] + j*s[2][l] + k*s[3][l]; tt = 0; for (int l = 1; l <= 3; l++) if (get[l] != 0 && s[0][l] != 0) { tt = get[l] / s[0][l]; break; } bool flag = true; for (int l = 1; l <= 3 && flag; l++) if (tt * s[0][l] != get[l]) flag = false; /* if (tt > 0 && check(1,2) && check(2,3) && check(3,1)) {// 也可用行列式去做 */ if (flag) { tot = i+j+k; totx = i; toty = j; totz = k; t = tt; } } if (tot == 300) printf("NONE/n"); else printf("%d %d %d %d/n", totx, toty, totz, t); return 0; }
2.本题可以用Cream方程组的知识去做,以下是USACO官方的解答When you combine multiples of mixtures, you can look at it as a multiplication of a matrix by a vector. For example, 8*(1:2:3) + 1*(3:7:1) + 5*(2:1:2) = (21:28:35) = 7*(3:4:5) can be seen as [ 1 3 2 ] [ 8 ] [ 2 7 1 ] * [ 1 ] = 7 [3 4 5] [ 3 1 2 ] [ 5 ] The matrix and the goal ratio vector (3:4:5 in this case) are given; what we have to find is the multiple vector (8:1:5 in this case) and the proportionality costant (7 here). This is like solving a system of linear equations. We can write it as AX = kB. Now, if we use Cramer's Rule, and let D = determinant of A, then X_1 = k D_1 / D X_2 = k D_2 / D X_3 = k D_3 / D, where D_1 is the determinant of the matrix A with the 1st column is replaced by B, D_2 is the determinant of the matrix A with the 2nd column is replaced by B, D_3 is the determinant of the matrix A with the 3rd column is replaced by B. (see a Linear Algebra textbook on why this works.) ,P> We are looking for integral solutions. If D = 0, no solutions. Otherwise, let k = D, and then X_1 = D_1, etc. If these values (X_1,X_2,X_3, _and_ k) all have a greatest common factor above 1, divide them all by that factor, as we are looking for the smallest possible solutions. Now, if some of the numbers is greater than 100, we have not found a feasible solution, so we output `NONE'. Otherwise, the triple (X_1,X_2,X_3) is output, as well as the value k.
题目五:Magic Squares
算法:BFS
1.康托展开(Cantor),给出一个1-N的排列,求1-N的全排列字典序中的位置;
2.康托展开的逆运算是给出位置,找到这个排列。
3.这道题要用Cantor + Hash可以有效节约空间
#include <stdio.h> #include <string.h> const int d[3][8] = {{7,6,5,4,3,2,1,0}, {3,0,1,2,5,6,7,4}, {0,6,1,3,4,2,5,7}}, max = 40321; int fra[8]; int q[max][8], best[max], choose[max], pre[max], path[100]; bool vis[max], cov[9]; int head = 0, tail = 0, now[8]; int hash(int *a) { int num = 0; memset(cov,0,sizeof(cov)); for (int i = 0; i < 8; i++) { for (int j = 1; j < a[i]; j++) if (!cov[j]) num += fra[7-i]; cov[a[i]] = 1; } return num; } void check() { int num = hash(now); if (!vis[num]) { tail++; best[tail] = best[head]+1; vis[num] = 1; } } int main(){ freopen("msquare.in", "r", stdin); freopen("msquare.out", "w", stdout); fra[0] = 1; for (int i = 1; i <= 7; i++) fra[i] = fra[i-1]*i; for (int i = 0; i <= 7; i++) scanf("%d", &q[0][i]); best[0] = 0; memset(vis,0,sizeof(vis)); vis[0] = 1; pre[0] = -1; int final = hash(q[0]); for (int i = 0 ; i <= 7; i++) q[0][i] = i+1; //printf("%d/n", final); if (final == 0) printf("0/n/n"); else//why??? while (head <= tail) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 8; j++) now[j] = q[head][d[i][j]]; int num = hash(now); if (!vis[num]) { tail++; vis[num] = true; for (int j = 0; j < 8; j++) q[tail][j] = now[j]; best[tail] = best[head] + 1; pre[tail] = head; choose[tail] = i; if (num == final) break; } } if (vis[final]) { int len = 0; printf("%d/n", best[tail]); while (tail != 0) { len++; path[len] = choose[tail]; tail = pre[tail]; } for (int i = len; i > 0; i--) { printf("%c", path[i]+'A'); len++; if (len % 60 == 0 || i == 1) printf("/n"); } } head++; } return 0; }
题目六:Sweet Butter
算法:SPFA
题目大意:给出一张图,有一些人在某些点,求出最小的集合代价。
由于这张图边比较稀疏,用邻接表存好,且访问起来更方便;
用SPFA分别求出任意两点间的最短路。
/* ID: zhangji42 TASK: butter LANG: C++ */ #include <stdio.h> #include <string.h> const int maxn = 801, maxcow = 501, maxedge = 1451*2; int cow[maxcow], first[maxn]; int v[maxedge], w[maxedge], next[maxedge]; int q[maxedge*5], best[maxn], vis[maxn]; int main(){ freopen("butter.in", "r", stdin); freopen("butter.out", "w", stdout); int M, N, C; scanf("%d %d %d", &M, &N, &C); for (int i = 1; i <= M; i++) scanf("%d", &cow[i]); memset(first,-1,sizeof(first)); int u; for (int i = 1; i <= C; i++) { scanf("%d %d %d", &u, &v[i], &w[i]); next[i] = first[u]; first[u] = i; v[C+i] = u; w[C+i] = w[i]; next[C+i] = first[v[i]]; first[v[i]] = C+i; } int min = 1 << 30; for (int i = 1; i <= N; i++) { int head = 0, tail = 0, now; q[head] = i; memset(vis,0,sizeof(vis)); vis[i] = 1; for (int j = 1; j <= N; j++) best[j] = 1 << 29; best[i] = 0; while (head <= tail) { now = q[head]; for (int k = first[now]; k != -1; k = next[k]) if (best[now] + w[k] < best[v[k]]) { best[v[k]] = best[now] + w[k]; if (!vis[v[k]]) { q[++tail] = v[k]; vis[v[k]] = 1; } } vis[now] = 0; head++; } now = 0; for (int j = 1; j <= M; j++) {now += best[cow[j]];}//printf("%d %d/n", cow[j], best[cow[j]]);} if (now < min) min = now; } printf("%d/n", min); return 0; }
很好,这一个星期的任务完成了,不过接下来还要奋战学校的OJ啊!加油!