USACO 3.3

3.3开始的文字介绍是欧拉通路和欧拉回路。类似于小时候学过的一笔画问题。介绍里伪代码写的很清晰,几乎照着拍就可以A题。

Riding The Fences:TEXT里的例题,欧拉路径。TEXT里说因为递归的层数比较深,所以要自己定义栈。我这里就用循环写了。其实递归也可以过。

 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 }
View Code

Shopping Offers:一个比较简单但是写起来很复杂的dp,要开5个维度的数组。。。物品数其实不超过5个- -。我用dfs+记忆化写的。

 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 }
View Code

Camelot:本节最复杂最难搞的题了。本质是bfs,但是要想一下复杂度。基本想法是这样:地图大概1000个点,先枚举终点。对于每个终点,可以假设有1个骑士带王走,其他的不带。计算出每个骑士带王和不代王需要花费的步数,再枚举每个骑士带王走的情况(注意还要考虑王自己走到终点的情况)。计算每个骑士带王的时候,可以枚举骑士和王重合点。但是这样一来,要枚举终点、重合点以及骑士,复杂度是O(r*c*KnightNumber*r*c),大概是1000^3,要超时了。可以考虑王和骑士重合点不超过王所在位置±2的范围内(也就是25个点),因此重合点可以被认为是常数,复杂度降到O(r*c*Knight*25),可做。

注意大坑:输入是先列数再行数!这里调了一天- -。

  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 }
View Code

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的个数,就可以拿来判断该形状里是否有坏点。这种计算矩形内数字和的方法非常经典。

 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 }
View Code

A Game:这题也是比较水的题。很容易想到dp的策略:dp[i][j]表示从第i个数字到第j个数字区域先手最大能拿多少分,通过dp[i+1][j]和dp[i][j-1]可以O(1)算出。需要预处理出sum[i]表示开头到i的和,这样可以O(1)求出任意区间的和。

 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 }
View Code
原文地址:https://www.cnblogs.com/lzsz1212/p/3180612.html