2016 ACM/ICPC亞洲區青島站現場賽(部分題解)
摘要
本文主要列舉並求解了2016 ACM/ICPC亞洲區青島站現場賽的部分真題,著重介紹了各個題目的解題思路,結合詳細的AC代碼,意在熟悉青島賽區的出題策略,以備戰2018青島站現場賽。
HDU 5984 Pocky
題意
給出一根棒子(可以吃的)的長度x和切割過程中不能小於的長度d,每次隨機的選取一個位置切開,吃掉左邊的一半,對右邊的棒子同樣操作,直至剩余的長度不大於d時停止。現在給出x和d,問切割次數的數學期望是多少。
解題思路
當看到第二個樣例2 1時,結果是1.693147,聯想到ln(2) = 0.693147,可猜測當x > d時,答案是ln(x/d) + 1。
詳細解法:
設長度為x、限制長度是d的棒切割次數的數學期望是f(x),首先當x < d時,f(x) = 0(直接結束,切割次數為0);當x >= d時,f(x) 應該是任選一點後,右邊部分切割次數的數學期望加上1。設t是切割的位置,即,其中後面的式子表示切割點t的數學期望(積分0到x,取到這一點的概率乘上t的概率密度,也就是長度為t的切割次數的數學期望),進而又可以寫成(積分中,系數可以自由進出),也即將f(x)寫成如下形式
由此可得f(x) = ln(x) + c,當x = d時,f(d) = ln(d) + c = 1得,c = 1 - ln(d),代入f(x) = ln(x) - ln(d) + 1,也即f(x) = ln(x/d) + 1;
綜上所述
代碼如下:
其中涉及C語言中對數的表示方法,C中只定義兩log(double x)和log10(double x),分別表示數學中的ln和lg,至於如何表示loga(b)呢?使用換底公式log(b)/log(a)即可。
1 #include <cstdio> 2 #include <cmath> 3 4 int main() 5 { 6 double x, d; 7 int T; 8 scanf("%d", &T); 9 while(T--) { 10 scanf("%lf%lf", &x, &d); 11 if(x <= d) 12 printf("0.000000\n"); 13 else 14 printf("%.6lf\n", log(x) - log(d) + 1); 15 } 16 return 0; 17 }
HDU 5983 Pocket Cube
題意
輸入一個二階魔方的狀態,問能否一步將其復原。
解題思路
需要細心和耐心,考慮每一種擰法,操作的時候,先順時針改變一個面的數,然後改變四周的數,寫出操作模板。要特別註意輸入狀態的次序,哪個面先,以及哪個角先。
代碼如下:
1 #include <cstdio> 2 3 struct Magic2{ 4 int f[5], b[5], u[5], d[5], l[5], r[5]; 5 void get_u() {for(int i = 1 ; i <= 4; i++) {scanf("%d", &u[i]);}} 6 void get_d() {for(int i = 1 ; i <= 4; i++) {scanf("%d", &d[i]);}} 7 void get_f() {for(int i = 1 ; i <= 4; i++) {scanf("%d", &f[i]);}} 8 void get_b() {for(int i = 1 ; i <= 4; i++) {scanf("%d", &b[i]);}} 9 void get_l() {for(int i = 1 ; i <= 4; i++) {scanf("%d", &l[i]);}} 10 void get_r() {for(int i = 1 ; i <= 4; i++) {scanf("%d", &r[i]);}} 11 void L(int cnt) { 12 for(; cnt > 0; cnt--) { 13 int a[5]; 14 for(int i = 1; i <= 4; i++) a[i] = l[i]; 15 l[2] = a[1];l[1] = a[3]; 16 l[3] = a[4];l[4] = a[2]; 17 18 int x = b[3], y = b[1]; 19 b[3] = d[3], b[1] = d[1]; 20 d[3] = f[3], d[1] = f[1]; 21 f[3] = u[3], f[1] = u[1]; 22 u[3] = x, u[1] = y; 23 } 24 } 25 void R(int cnt) { 26 for(; cnt > 0; cnt--) { 27 int a[5]; 28 for(int i = 1; i <= 4; i++) a[i] = r[i]; 29 r[1] = a[3], r[2] = a[1]; 30 r[3] = a[4], r[4] = a[2]; 31 32 int x = b[2], y = b[4]; 33 b[2] = u[2], b[4] = u[4]; 34 u[2] = f[2], u[4] = f[4]; 35 f[2] = d[2], f[4] = d[4]; 36 d[2] = x, d[4] = y; 37 } 38 } 39 void U(int cnt) { 40 for(; cnt > 0; cnt--) { 41 int a[5]; 42 for(int i = 1; i <= 4; i++) a[i] = u[i]; 43 u[1] = a[3], u[2] = a[1]; 44 u[3] = a[4], u[4] = a[2]; 45 46 int x = b[3], y = b[4]; 47 b[3] = l[4], b[4] = l[2]; 48 l[4] = f[2], l[2] = f[1]; 49 f[2] = r[1], f[1] = r[3]; 50 r[1] = x, r[3] = y; 51 } 52 } 53 void D(int cnt) { 54 for(; cnt > 0; cnt--) { 55 int a[5]; 56 for(int i = 1; i <= 4; i++) a[i] = d[i]; 57 d[2] = a[1], d[1] = a[3]; 58 d[4] = a[2], d[3] = a[4]; 59 60 int x = b[1], y = b[2]; 61 b[1] = r[4], b[2] = r[2]; 62 r[4] = f[3], r[2] = f[4]; 63 f[3] = l[1], f[4] = l[3]; 64 l[1] = x, l[3] = y; 65 } 66 } 67 void F(int cnt) { 68 for(; cnt > 0; cnt--) { 69 int a[5]; 70 for(int i = 1; i <= 4; i++) a[i] = f[i]; 71 f[1] = a[3], f[2] = a[1]; 72 f[3] = a[4], f[4] = a[2]; 73 74 int x = u[3], y = u[4]; 75 u[3] = l[3], u[4] = l[4]; 76 l[3] = d[1], l[4] = d[2]; 77 d[1] = r[3], d[2] = r[4]; 78 r[3] = x, r[4] = y; 79 } 80 } 81 void B(int cnt) { 82 for(; cnt > 0; cnt--) { 83 int a[5]; 84 for(int i = 1; i <= 4; i++) a[i] = u[i]; 85 u[1] = a[3], u[3] = a[4]; 86 u[2] = a[1], u[4] = a[2]; 87 88 int x = u[1], y = u[2]; 89 u[1] = r[1], u[2] = r[2]; 90 r[1] = d[4], r[2] = d[3]; 91 d[4] = l[1], d[3] = l[2]; 92 l[1] = x, l[2] = y; 93 } 94 } 95 bool ok() { 96 for(int i = 2; i <= 4; i++) { 97 if(u[i] != u[1] || d[i] != d[1] 98 || l[i] != l[1] || r[i] != r[1] 99 || f[i] != f[1] || b[i] != b[1]) 100 return 0; 101 } 102 return 1; 103 } 104 bool operate(char ch) { 105 if(ch == ‘u‘) { 106 U(1); 107 if(ok()) 108 return 1; 109 else { 110 U(3); 111 U(3); 112 if(ok()) 113 return 1; 114 else{ 115 U(1); 116 return 0; 117 } 118 } 119 } 120 if(ch == ‘d‘) { 121 D(1); 122 if(ok()) 123 return 1; 124 else{ 125 D(3); 126 D(3); 127 if(ok()) 128 return 1; 129 else{ 130 D(1); 131 return 0; 132 } 133 } 134 } 135 if(ch == ‘f‘) { 136 F(1); 137 if(ok()) 138 return 1; 139 else{ 140 F(3); 141 F(3); 142 if(ok()) 143 return 1; 144 else{ 145 F(1); 146 return 0; 147 } 148 } 149 } 150 if(ch == ‘b‘) { 151 B(1); 152 if(ok()) 153 return 1; 154 else{ 155 B(3); 156 B(3); 157 if(ok()) 158 return 1; 159 else{ 160 B(1); 161 return 0; 162 } 163 } 164 } 165 if(ch == ‘l‘) { 166 L(1); 167 if(ok()) 168 return 1; 169 else{ 170 L(3); 171 L(3); 172 if(ok()) 173 return 1; 174 else{ 175 L(1); 176 return 0; 177 } 178 } 179 } 180 if(ch == ‘r‘) { 181 R(1); 182 if(ok()) 183 return 1; 184 else{ 185 R(3); 186 R(3); 187 if(ok()) 188 return 1; 189 else{ 190 R(1); 191 return 0; 192 } 193 } 194 } 195 } 196 void print() { 197 puts("###"); 198 for(int i = 1; i <= 4; i++) printf("%d ", u[i]); puts(""); 199 for(int i = 1; i <= 4; i++) printf("%d ", f[i]); puts(""); 200 for(int i = 1; i <= 4; i++) printf("%d ", d[i]); puts(""); 201 for(int i = 1; i <= 4; i++) printf("%d ", b[i]); puts(""); 202 for(int i = 1; i <= 4; i++) printf("%d ", l[i]); puts(""); 203 for(int i = 1; i <= 4; i++) printf("%d ", r[i]); puts(""); 204 } 205 }m2; 206 207 int main() 208 { 209 int T; 210 scanf("%d", &T); 211 while(T--) { 212 m2.get_u(); 213 m2.get_f(); 214 m2.get_d(); 215 m2.get_b(); 216 m2.get_l(); 217 m2.get_r(); 218 if(m2.ok() || m2.operate(‘u‘) || m2.operate(‘d‘) || m2.operate(‘l‘) 219 || m2.operate(‘r‘) || m2.operate(‘f‘) || m2.operate(‘b‘)) 220 printf("YES\n"); 221 else 222 printf("NO\n"); 223 } 224 return 0; 225 }
HDU 5985 Lucky Coins
題意
給出n個硬幣和每個硬幣的數量和正面朝上的概率,問每個硬幣成為幸運硬幣的概率是多少。成為幸運硬幣的條件是每投一次將所有背面朝上的硬幣去掉,繼續拋擲,直至剩下一種或者一個都剩下,那最後一種留下的硬幣就是幸運硬幣。
解題思路
概率DP,我們定義dead[i][j]表示第i種硬幣在前j步以內全部被拋棄的概率,顯然,
化簡可得 .
那麽我們定義aliv[i][j] 表示第i種硬幣在前j步以內至少有一個沒有被拋棄的概率是 1 - dead[i][j],那麽第i個硬幣成為幸運硬幣的概率大概等於(應為當k = 30的時候0.5的三十次方就很小),其實際意義就是第i種硬幣成為幸運硬幣的概率等於模擬投擲100次,而每次讓第1到n種硬幣在k步全部被拋棄的概率乘上第i種硬幣在第k步至少還有一個而第k+1步全部被拋棄的概率,當然前面的第1到第n種硬幣全部被拋棄不包括第i種硬幣,故完整的式子是:
代碼如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 6 using namespace std; 7 8 const int maxn = 15; 9 int n; 10 double num[maxn], p[maxn], ans[maxn]; 11 double dead[maxn][110], alive[maxn][110]; 12 13 void cdead() { 14 for(int k = 1; k <= 100; k++) { 15 for(int i = 0; i < n; i++) { 16 dead[i][k] = pow(1.0 - pow(p[i], k), num[i]); 17 } 18 } 19 } 20 void calive() { 21 for(int k = 1; k <= 100; k++) { 22 for(int i = 0; i < n; i++) { 23 alive[i][k] = 1.0 - dead[i][k]; 24 } 25 } 26 } 27 28 int main() 29 { 30 int T; 31 scanf("%d", &T); 32 while(T--) { 33 scanf("%d", &n); 34 for(int i = 0; i < n; i++) { 35 scanf("%lf%lf", &num[i], &p[i]); 36 } 37 if(n == 1) { 38 printf("1.000000\n"); 39 continue; 40 } 41 42 cdead(); 43 calive(); 44 memset(ans, 0, sizeof(ans)); 45 for(int k = 1; k <= 100; k++) { 46 for(int i = 0; i < n; i++) { 47 double tmp = 1; 48 for(int j = 0; j < n; j++) { 49 if(j == i) continue; 50 tmp *= dead[j][k]; 51 } 52 ans[i] += tmp * (alive[i][k] - alive[i][k + 1]); 53 } 54 } 55 56 for(int i = 0; i < n; i++) { 57 printf("%.6lf%c", ans[i], i == n - 1 ? ‘\n‘ : ‘ ‘); 58 } 59 } 60 return 0; 61 }
可以看出青島站的題目還是有難度的,主要側重的是數學推理,準備時應該多以數學推理為主,大戰在即,加油!
2016 ACM/ICPC亞洲區青島站現場賽(部分題解)