3.3开始的文字介绍是欧拉通路和欧拉回路。类似于小时候学过的一笔画问题。介绍里伪代码写的很清晰,几乎照着拍就可以A题。
Riding The Fences:TEXT里的例题,欧拉路径。TEXT里说因为递归的层数比较深,所以要自己定义栈。我这里就用循环写了。其实递归也可以过。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # include <stdio.h> 2 # include <string.h> 3 4 5 int g[510][510], stack[110], top ; 6 int degree[510] ; 7 int EulerCircuit[1100] ; 8 9 10 void FindEuler(int s) 11 { 12 int i, ec = 0 ; 13 top = 0 ; 14 stack[top++] = s ; 15 while (top) 16 { 17 s = stack[--top] ; 18 if (degree[s] == 0){ 19 EulerCircuit[ec++] = s ; 20 continue ; 21 } 22 //else 23 stack[top++] = s ; 24 for (i = 1 ; i <= 500 ; i++)if (g[s][i]) 25 { 26 g[s][i]--, g[i][s]--, degree[s]--, degree[i]-- ; 27 stack[top++] = i ; 28 break ; 29 } 30 } 31 } 32 33 34 int main () 35 { 36 int a, b, i, f ; 37 freopen ("fence.in", "r", stdin) ; 38 freopen ("fence.out", "w", stdout) ; 39 while (~scanf ("%d", &f)) 40 { 41 memset (g, 0, sizeof(g)) ; 42 memset (degree, 0, sizeof(degree)) ; 43 for (i = 0 ; i < f ; i++) 44 scanf ("%d%d", &a, &b), g[a][b]++, g[b][a]++, degree[a]++, degree[b]++ ; 45 for (i = 1 ; i <= 500 ; i++) if (degree[i]%2==1) break ; 46 if (i > 500) i = 1 ; 47 FindEuler(i) ; 48 for (i = f ; i >= 0 ; i--) 49 printf ("%d ", EulerCircuit[i]) ; 50 } 51 return 0 ; 52 }
Shopping Offers:一个比较简单但是写起来很复杂的dp,要开5个维度的数组。。。物品数其实不超过5个- -。我用dfs+记忆化写的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # include <stdio.h> 2 # include <string.h> 3 4 5 int vv[1010] ; 6 int idx[110], num[110], prc[110], s ; 7 int c[510], k[510]; 8 int cs[10], ks[10], ps[10], b ; 9 int dp[6*6*6*6*6] ; 10 int INF = 0x0f0f0f0f ; 11 12 13 int xy(int a[]) 14 { 15 int rtn = 0, i ; 16 for (i = 0 ; i < b ; i++) 17 rtn = rtn * 6 + a[i] ; 18 return rtn ; 19 } 20 21 22 int min(int a, int b){return a<b?a:b;} 23 24 25 int dfs (int a[]) 26 { 27 int i, j, rtn = INF, flag, pos ; 28 if (dp[xy(a)] != -1) return dp[xy(a)] ; 29 30 for (i = 0 ; i < b ; i++) if (a[i] != 0) 31 { 32 a[i] -- ; 33 rtn = min(rtn, dfs (a) + ps[i]) ; 34 a[i] ++ ; 35 } 36 37 for (i = 0 ; i < s ; i++) 38 { 39 flag = 0 ; 40 for (j = idx[i] ; j < idx[i]+num[i] ; j++) 41 { 42 pos = vv[c[j]] ; 43 if (pos == -1) flag = 1 ; 44 else 45 { 46 a[pos] -= k[j] ; 47 if (a[pos] < 0) flag = 1 ; 48 } 49 } 50 if (flag == 0) 51 rtn = min(rtn, dfs (a)+prc[i]) ; 52 for (j = idx[i] ; j < idx[i]+num[i] ; j++) 53 { 54 pos = vv[c[j]] ; 55 if (pos != -1) 56 a[pos] += k[j] ; 57 } 58 59 } 60 return dp[xy(a)] = rtn ; 61 } 62 63 64 int main () 65 { 66 int cc, i, j, n ; 67 freopen ("shopping.in", "r", stdin) ; 68 freopen ("shopping.out", "w", stdout) ; 69 while (~scanf ("%d", &s)) 70 { 71 for (i = 0, cc = 0 ; i < s ; i++) 72 { 73 idx[i] = cc ; 74 scanf ("%d", &n) ; 75 num[i] = n ; 76 for (j = 0 ; j < n; j++) 77 scanf ("%d%d", &c[cc], &k[cc]), cc++ ; 78 scanf ("%d", &prc[i]) ; 79 } 80 memset (vv, -1, sizeof (vv)) ; 81 scanf ("%d", &b) ; 82 for (i = 0 ;i < b ; i++) 83 { 84 scanf ("%d%d%d", &cs[i], &ks[i], &ps[i]) ; 85 vv[cs[i]] = i ; 86 } 87 memset (dp, -1, sizeof (dp)) ; 88 dp[0] = 0 ; 89 printf ("%d ", dfs (ks)) ; 90 } 91 return 0 ; 92 }
Camelot:本节最复杂最难搞的题了。本质是bfs,但是要想一下复杂度。基本想法是这样:地图大概1000个点,先枚举终点。对于每个终点,可以假设有1个骑士带王走,其他的不带。计算出每个骑士带王和不代王需要花费的步数,再枚举每个骑士带王走的情况(注意还要考虑王自己走到终点的情况)。计算每个骑士带王的时候,可以枚举骑士和王重合点。但是这样一来,要枚举终点、重合点以及骑士,复杂度是O(r*c*KnightNumber*r*c),大概是1000^3,要超时了。可以考虑王和骑士重合点不超过王所在位置±2的范围内(也就是25个点),因此重合点可以被认为是常数,复杂度降到O(r*c*Knight*25),可做。
注意大坑:输入是先列数再行数!这里调了一天- -。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # include <stdio.h> 2 3 4 #define STEP 2 5 #define NS ((2*STEP+1)*(2*STEP+1)) 6 int tab[NS+10][2] ; 7 int ktab[8][2] = {{-2,-1}, {-2,1}, {-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}} ; 8 int n, m ; 9 int nx[1010], ny[1010], kx, ky, cc ; 10 int end[35][35] ; 11 int king[30][35][35] ; 12 int noking[1010], takeking[1010] ; 13 int INF = 0x0f0f0f0f ; 14 int q[1010][2] ; 15 16 17 int mymax(int a,int b){return a>b?a:b;} 18 int mymin(int a, int b){return a<b?a:b;} 19 int myabs(int x){return x<0?-x:x;} 20 int kingdist(int x, int y){return mymax(myabs(x-kx), myabs(y-ky)) ;} 21 22 23 void bfs (int x, int y, int g[35][35]) 24 { 25 int f, r, i, j, xx, yy ; 26 for (i = 1 ; i<= n ; i++) 27 for (j = 1 ; j <= m ; j++) 28 g[i][j] = INF ; 29 g[x][y] = 0 ; 30 q[0][0] = x, q[0][1] = y ; 31 for (f = 0,r=1 ; f < r ; f++) 32 { 33 x = q[f][0], y = q[f][1] ; 34 for (i = 0 ; i < 8 ; i++) 35 { 36 xx = x + ktab[i][0], yy = y + ktab[i][1] ; 37 if (xx <= 0 || xx > n || yy <= 0 || yy > m) continue ; 38 if (g[xx][yy] != INF) continue ; 39 g[xx][yy] = g[x][y] + 1 ; 40 q[r][0]=xx, q[r][1] = yy, r++ ; 41 } 42 } 43 } 44 45 46 int gao(int ex, int ey) 47 { 48 int xx, yy, sum, ans ; 49 int i, j ; 50 bfs (ex, ey, end) ; 51 52 for (i = 0 ; i < NS ; i++){ 53 xx = kx + tab[i][0], yy = ky + tab[i][1] ; 54 if (xx >= 1 && xx <= n && yy >= 1 && yy <= m) 55 bfs (xx, yy, king[i]) ; 56 } 57 58 59 for (i = 0 ; i < cc ; i++) 60 { 61 noking[i] = end[nx[i]][ny[i]] ; 62 takeking[i] = INF ; 63 for (j = 0 ; j < NS ; j++){ 64 xx = kx + tab[j][0], yy = ky + tab[j][1] ; 65 if (xx >= 1 && xx <= n && yy >= 1 && yy <= m) 66 takeking[i] = mymin( takeking[i], 67 king[j][nx[i]][ny[i]] + kingdist(xx,yy) 68 + end[xx][yy]) ; 69 } 70 } 71 72 sum = 0; 73 ans = kingdist(ex, ey) ; 74 for (i = 0 ; i < cc ; i++) 75 ans += noking[i], sum += noking[i] ; 76 77 for (i = 0 ; i < cc ; i++) 78 ans = mymin(ans, sum - noking[i] + takeking[i]) ; 79 80 return ans ; 81 } 82 83 84 int main () 85 { 86 char xchar ; 87 int yint, i, j, ans ; 88 freopen ("camelot.in", "r", stdin) ; 89 freopen ("camelot.out", "w", stdout) ; 90 91 scanf ("%d %d%*c", &m, &n) ; 92 scanf ("%c %d%*c", &xchar, &yint) ; 93 ans = 0 ; 94 for (i = -STEP ; i <= STEP ; i++) 95 for (j = -STEP ; j<= STEP ; j++) 96 tab[ans][0] = i, tab[ans++][1] = j ; 97 kx = xchar-'A'+1, ky = yint ; 98 cc = 0 ; 99 100 while (~scanf ("%c %d%*c", &xchar, &yint)) 101 nx[cc] = xchar-'A'+1, ny[cc] = yint, cc++ ; 102 103 ans = INF ; 104 for (i = 1 ; i <= n ; i++) 105 for (j = 1 ; j <= m ; j++) 106 ans = mymin(ans, gao(i,j)) ; 107 108 printf ("%d ", ans) ; 109 return 0 ; 110 }
Home on the Range:这题是非常水的题了。可以开一个数组num[i][j]表示从左上角到(i,j)这个矩形区域内1的个数。从左上到右下num[i][j]可以利用前面的结果O(1)求出:num[i][j] = num[i-1][j]+num[i][j-1]-num[i-1][j-1]。然后枚举每一个正方形,通过num[i][j]也可以O(1)求出正方形内1的个数,就可以拿来判断该形状里是否有坏点。这种计算矩形内数字和的方法非常经典。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # include <stdio.h> 2 3 4 char gg[300][300] ; 5 int g[300][300] ; 6 int ans[300] ; 7 8 int main () 9 { 10 int i, j, len, p, num ; 11 freopen ("range.in", "r", stdin) ; 12 freopen ("range.out", "w", stdout) ; 13 14 while (~scanf ("%d", &p)) 15 { 16 for (i = 0 ; i < 300 ; i++) ans[i] = 0 ; 17 for(i = 1 ; i <= p ; i++) 18 scanf("%s", gg[i]+1) ; 19 for (i =1 ; i <= p ;i++) 20 for (j = 1 ; j <= p ;j++) 21 g[i][j] = g[i-1][j]+g[i][j-1]-g[i-1][j-1] + (gg[i][j]-'0') ; 22 23 for (i = 1 ; i <= p ; i++) 24 for (j = 1 ;j <= p ; j++) 25 { 26 for (len = 2 ; len <= p-i+1 && len <= p-j+1 ; len++) 27 { 28 num = g[i+len-1][j+len-1]+g[i-1][j-1]-g[i+len-1][j-1]-g[i-1][j+len-1] ; 29 if (num != len*len) break ; 30 ans[len]++ ; 31 } 32 } 33 for (i = 2 ; i <= p ; i++) if (ans[i] != 0) 34 printf ("%d %d ", i, ans[i]) ; 35 } 36 return 0 ; 37 }
A Game:这题也是比较水的题。很容易想到dp的策略:dp[i][j]表示从第i个数字到第j个数字区域先手最大能拿多少分,通过dp[i+1][j]和dp[i][j-1]可以O(1)算出。需要预处理出sum[i]表示开头到i的和,这样可以O(1)求出任意区间的和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # include <stdio.h> 2 # include <string.h> 3 4 5 int a[110], sum[110], dp[110][110] ; 6 int max(int a, int b){return a>b?a:b;} 7 8 9 int dfs (int s, int e) 10 { 11 int ans1, ans2 ; 12 if (s == e) return a[s] ; 13 if (dp[s][e] != -1) return dp[s][e] ; 14 15 ans1 = a[s] + sum[e] - sum[s] - dfs (s+1,e) ; 16 ans2 = a[e] + sum[e-1] - sum[s-1] - dfs (s, e-1) ; 17 return dp[s][e] = max(ans1, ans2) ; 18 } 19 20 21 int main() 22 { 23 int n , i ; 24 freopen ("game1.in", "r", stdin) ; 25 freopen ("game1.out", "w", stdout) ; 26 while (~scanf ("%d", &n)) 27 { 28 for (i = 1 ; i <= n; i++) 29 scanf ("%d", &a[i]), sum[i] = sum[i-1] + a[i] ; 30 memset (dp, -1, sizeof(dp)) ; 31 dfs (1,n) ; 32 printf ("%d %d ", dp[1][n], sum[n]-dp[1][n]) ; 33 } 34 return 0 ; 35 }