1. 程式人生 > 其它 >11.3 校內模擬賽

11.3 校內模擬賽

總結:

祭考試做出一道期望 dp /se

考試心路歷程:

5 min 讀完 T1, ahhhhhh,簽到題,10 min 寫完,一發過大樣例。

8:20 開幹 T2,期望 dp,讀完題,感覺可做。

寫了好久,調過樣例,寫了對拍,一拍就掛,rnm。

調半天,轉移寫掛 = =

10:10 開 T3

T3 題目好奇怪,為啥開頭是 T2 的題目啊,沒用,沒用,刪了。

艹,這花店在哪兒,我家在哪兒,我咋找不到我家!!找不到家的孩子

讀了好幾遍,沒讀懂題,麻了,睡覺。

賽後:為啥我 T3 題目少了一個自然段啊 !!!!

T1 三向城

題面

solution

手摸題目,你發現這是個滿二叉樹。

然後你會發現,他的層數其實很少。

你還會發現每個節點的父親就是改節點 / 2

然後你就暴力向上跳就好了 = =

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAX = 35;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int q, Ans;
int get(int x) {
  for (int i = 1; i <= MAX; i++) 
  	if((1 << i) > x) return i;	 
}
signed main(){
  //freopen("city.in", "r", stdin);
  //freopen("city.out", "w", stdout);
  q = read();
  for (int i = 1, x, y; i <= q; i++) {
     x = read(), y = read();
     Ans = 0;
	 int dep_x = get(x), dep_y = get(y);
	 if(dep_x < dep_y) swap(x, y), swap(dep_x, dep_y);
	 while(dep_x != dep_y) {
	   x = x / 2;
	   Ans++, dep_x--;
	 } 	
	 while(x != y) {
	   x = x / 2, y = y / 2;
	   Ans += 2;
	 }
	 cout<<Ans<<"\n";
  }
  return 0;
}

T2 靈魂畫師

題面

solution

期望 = 概率 * 權值

顯然權值就是顏色,考慮怎麼計算每個位置變成某個顏色的概率。

不難發現,某個位置變成哪個顏色只與它被選中的次數有關。

\(P_{i, j}\) 表示 \(i\) 一個點出現在 \(i\) 個區間內,被選中 \(j\) 次的概率。

顯然 \(P_{i, j} = \frac{C_{i}^{j}}{2}\)

\(f_{i, j}\) 表示選中 \(i\) 次,變為顏色 \(j\) 的概率。

\(f_{i + 1, j \times o \% c} += f_{i, j} / c\)

\(P\)\(f\) 都可以預處理出來。

\(Ans = \sum_{i = 1}^{i \leq n}\sum_{j = 0}^{cnt[i]}\sum_{o = 0}^{o < c} f_{j, o} \times P_{cnt[i], j} \times o\)

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 110;
const double eps = 1e-7;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int n, c, k, cnt[MAXN];
double C[MAXN][MAXN], P[MAXN][MAXN], Ans;
void Init_C() {
  C[0][0] = 1;
  for (int i = 1; i <= 100; i++) {
  	C[i][0] = 1;
  	C[i][0] = C[i][0] / 2.0;
  	for (int j = 0; j <= i; j++) {
	   C[i][j] = C[i - 1][j - 1] / 2.0 + C[i - 1][j] / 2.0;
    }
  }
}
void Init_P() {
  P[0][1] = 1;
  for (int i = 0; i <= k; i++) 
    for (int j = 0; j < c; j++) {
      if(P[i][j] <= eps) continue;
	  for (int o = 0; o < c; o++) {
	     P[i + 1][j * o % c] = (P[i + 1][j * o % c] + P[i][j] / c);
	   }	
    }
}
int main(){
  n = read(), c = read(), k = read();
  Init_C(), Init_P();
  for (int i = 1; i <= k; i++) {
    int x = read(), y = read();
    for (int j = x; j <= y; j++) cnt[j]++;
  } 
  for (int i = 1; i <= n; i++) 
  	for (int j = 0; j <= cnt[i]; j++) 
  	  for (int o = 0; o < c; o++) Ans += C[cnt[i]][j] * P[j][o] * o;
  printf("%.3lf\n", Ans);
  return 0;
}

T3 香子蘭

題面

solution

直接搬 szt 的了 /cy

你看這個資料範圍就知道要狀壓,你看它要求最短路徑就得狀壓最短路。

我們先跑個 \(Folyd\) 求出兩兩之間的最短距離。

我們在設兩個陣列 \(f[S][i],g[S][i]\) 分別表示以 \(1\) 作為出發點,已經進行收割/播種的狀態為 \(S\),最後一個點為 \(i\) 的最短距離,\(g\) 陣列則表示以 \(n\) 為出發點。

上面這個 \(f,g\) 陣列可以預處理出來。時間複雜度為 \(O(2^nn^2)\),這個複雜度有點危,我們發現我們不用列舉全部的,我們只需要列舉 \(S\)\(1\) 的個數 \(≤\frac{n−2}{2}+2\) 的狀態即可。複雜度就被我們優化掉一個 \(n\)(我們本質還是列舉所有子集,只不過在某些子集只做了 \(O(n)\) Check )

然後你列舉首先選擇哪 \(n−2\) 個花田收割,設列舉的收割的花田的狀態為 \(S\)

我們考慮收割的過程,考慮怎麼把前一半的最短路和後一半的最短路合併起來,那就是,列舉在集合中的點 \(i\) 和不在集合中的點 \(j\),設答案為 \(res\),則:

\(res=min\{f[S|1][i] + g[M \oplus (S|(1<<n−1))][j]+dis[i][j]\}\)
其中 \(M\) 表示所有點的全集。

這樣就保證了 \(S\) 狀態中選中的點一定會在前 \(\frac{n−2}{2}\) 花田裡收割,剩餘的點在後來收割。

同理,我們考慮播種的過程,其實就是反過來,我們再設一個播種的答案 \(ans\),則

\(ans=\min\{g[S|1][i]+f[M \oplus (S|(1<<n−1))][j]+dis[i][j]\}\)

最終答案就是 \(min\{res+ans\}\)

這個直接上搜索就行,控制一下搜尋上限,可以控制複雜度為 \(O(2^{\frac{n−2}{2}}\frac{n−2}{2}^2)\)

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 21;
const int INF = 0x3f3f3f3f3f;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int n, m, dis[MAXN][MAXN], N, M, Ans = 0x3f3f3f3f;
int f[MAXN][(1 << 20) - 1], g[MAXN][(1 << 20) - 1];
void Init_dis() {
  for (int i = 1; i <= n; i++) dis[i][i] = 0;
  for (int k = 1; k <= n; k++) 
    for (int i = 1; i <= n; i++) 
      for (int j = 1; j <= n; j++) 
        dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
void Init_F() {
  memset(f, 0x3f, sizeof f);
  f[1][1] = 0;
  for (int s = 0; s < (1 << n); s++) {
  	int cnt = 0;
  	for (int k = 1; k <= n; k++) if(s & (1 << k - 1)) cnt++;
	if (cnt > N + 2) continue; 
    for (int i = 1; i <= n; i++) {
      if(!(s & (1 << i - 1))) continue;
	  for (int j = 1; j <= n; j++) {
	  	if(s & (1 << j - 1)) continue;
	    f[j][s | (1 << j - 1)] = min(f[j][s | (1 << j - 1)], f[i][s] + dis[i][j]);
	  }
	}
  }
}
void Init_G() {
  memset(g, 0x3f, sizeof g);
  g[n][1 << n - 1] = 0;
  for (int s = 0; s < (1 << n); s++) {
  	int cnt = 0;
  	for (int k = 1; k <= n; k++) if(s & (1 << k - 1)) cnt++;
  	if(cnt > N + 2) continue;
  	for (int i = 1; i <= n; i++) { 
  	  if(!(s & (1 << i - 1))) continue;
  	  for (int j = 1; j <= n; j++) {
  	    if(s & (1 << j - 1)) continue;
		g[j][s | (1 << j - 1)] = min(g[j][s | (1 << j - 1)], g[i][s] + dis[i][j]);	 
	  }
	}
  } 
}
int In[MAXN], Out[MAXN], Incnt, Outcnt;
void dfs(int s, int pos, int cnt) {
   if(cnt > N) return ;
   if(pos == n) {
     if(cnt != N) return ;
     int ret_1 = INF, ret_2 = INF;
     for (int i = 1; i <= Incnt; i++) {
       for (int j = 1; j <= Outcnt; j++) {
       	 int x = In[i], y = Out[j];
       	 ret_1 = min(ret_1, f[x][s | 1] + g[y][(s | 1) ^ M] + dis[x][y]);
       	 ret_2 = min(ret_2, g[x][s | (1 << n - 1)] + f[y][(s | (1 << n - 1)) ^ M] + dis[x][y]);
	   }
	 }
	 Ans = min(Ans, ret_1 + ret_2);
     return ;	 
   }
   In[++Incnt] = pos;
   dfs(s | (1 << pos - 1), pos + 1, cnt + 1);
   Incnt--;
   Out[++Outcnt] = pos;
   dfs(s, pos + 1, cnt);
   Outcnt--;
}
int main(){
  n = read(), m = read();
  N = (n - 2) / 2, M = (1 << n) - 1;
  memset(dis, 0x3f, sizeof dis);
  for (int i = 1, u, v, w; i <= m; i++) {
  	 u = read() + 1, v = read() + 1, w = read();
  	 dis[u][v] = dis[v][u] = min(dis[u][v], w);
  }
  Init_dis();
  if(n == 3) {
  	 cout<<(dis[1][2] + dis[2][3]) * 2;
  	 return 0;
  }
  Init_F(), Init_G();
  dfs(0, 2, 0);
  cout<<Ans;
  return 0;
}