18寒假12測
Day1
題目名稱 |
party |
array |
treasure |
輸入 |
party.in |
array.in |
treasure.in |
輸出 |
party.out |
array.out |
treasure.out |
每個測試點時限 |
1秒 |
1秒 |
1秒 |
內存限制 |
64MB |
64MB |
64MB |
測試點數目 |
10 |
10 |
10 |
每個測試點分值 |
10 |
10 |
10 |
是否有部分分 |
無 |
無 |
無 |
題目類型 |
傳統 |
傳統 |
傳統 |
party
題目描述:
在M公司裏,每個人只有一個直屬上司(除了boss)。這個公司舉辦派對,每個人可以給派對帶來一定的歡樂值,但是每個人不能和自己的上司同時參加這個派對,求派對的最大歡樂值。
輸入:
第一行n表示公司有n個人。
第二行n個數,表示每個人的上司是誰,如果這個人的上司為0,說明這個人是boss。
第三行n個數,表示每個人的歡樂值為wi。
輸出:
一行一個數表示最大歡樂值。
樣例輸入:
6
0 1 1 1 4 4
1 1 1 1 1 1
樣例輸出:
4
解釋:2 3 5 6 同時出席。
數據規模:
對於30%的數據,n<=20。
對於100%的數據,n<= 1000000,0<=w[i]<=1000。
題解:樹形dp,dp[i][0/1]表示i號節點來不來,若父親來,兒子肯定不來,若父親不來,兒子隨便來不來;
dp[i][0] = sum( max (dp[son[i]][0], dp[son[i]][1]); dp[i][1] = sum(dp[son[i]][0]);
#include<bits/stdc++.h> using namespace std; const int maxn = 1000005; int head[maxn],tot,dp[maxn][2]; struct edge{ int to,nxt; }G[maxn + 100]; void add(int u,intv){ G[++tot].nxt = head[u]; G[tot].to = v; head[u] = tot; } void dfs(int u, int f){ for(int i = head[u]; i; i = G[i].nxt){ int v = G[i].to; //if(v == f)continue; dfs(v, u); dp[u][1] += dp[v][0]; dp[u][0] += max(dp[v][0], dp[v][1]); } } int main(){ freopen("party.in","r",stdin); freopen("party.out","w",stdout); int n; scanf("%d",&n); for(int i = 1; i <= n; i++){ int fa; scanf("%d",&fa); add(fa, i); } for(int i = 1; i <= n; i++){ int w; scanf("%d",&w); dp[i][1] = w; } dfs(0, 0); cout<<dp[0][0]<<endl; }
array
題目描述:
給定兩個長度為n的排列p1和p2,求它們的最長公共子序列。
輸入:
第一行一個數n。
接下來兩個n個數分別表示p1和p2。
輸出:
一個數表示最長子序列的長度。
樣例輸入:
3
1 2 3
2 1 3
樣例輸出:
2
解釋及說明:
共有兩種方案,分別為子序列為13和子序列為23.
數據規模:
對於30%的數據,n<=2000.
對於100%的數據,n<=100000。
題解:由於是排列,所以上下元素一樣,且一個序列中無重復元素,我們就把一個序列的元素順序做一個映射,在第二個序列中作最長上升子序列
#include<bits/stdc++.h> using namespace std; const int maxn = 1000005; int d[maxn], mp[maxn], q[maxn], tail; int main(){ freopen("array.in","r",stdin); freopen("array.out","w",stdout); int n, a, b; scanf("%d",&n); for(int i = 1; i <= n; i++){ scanf("%d",&a); mp[a] = i; } for(int j = 1; j <= n; j++){ scanf("%d",&b); d[j] = mp[b]; } for(int i = 1; i <= n; i++){ if(d[i] > q[tail])q[++tail] = d[i]; else { int pos = lower_bound(q + 1, q + 1 + tail, d[i]) - q; q[pos] = d[i]; } } cout<<tail<<endl; }
treasure
題目描述:
小a可以攻打m座城堡,攻打每個城堡有不同的寶物,但是在攻打有些城堡前,需要攻打另外的城堡。小a想知道,能獲得的最多寶物是多少?
輸入:
第一行兩個數,n,m。分別表示有n個城堡,可以攻打m個。
接下來n行每行兩個數a,b,分別表示在攻打第i個城堡前需要攻打a,攻打後獲得b的寶物。
輸出:
一行一個數ans表示可以獲得的最大寶物數量。
樣例輸入
3 2
0 1
0 2
0 3
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
0 0
樣例輸出:
5
13
數據規模:
對於30%的數據,n<=20,m<=20。
對於100%的數據,n<=500,m<=500,b[i]<=1000。
題解:樹形DP,這道題很像樹上染色,但不用考慮子樹和外界 聯系,要簡單一點;
dp[i][j]表示給i為根的子樹分配j次攻打機會可獲得最大的利益,答案就為dp[root][m];
由於必須打父親,才能打兒子,所以兒子可分配的機會為j-1,但0號點是我們假設的城堡,故他的子樹有j次機會,特判一下,然後做一個分組背包就好了,因為一個子樹分配的機會只能有一種方案;
抽象一下:每個子樹為一組,可供選擇的物品為min(siz[father], j), 容量為min(siz[son],j);
#include<bits/stdc++.h> using namespace std; const int maxn = 505; int n,m; int head[maxn],tot,dp[maxn][maxn],siz[maxn]; struct edge{ int to,nxt,w; }G[maxn + 100]; void add(int u,int v,int w){ G[++tot].nxt = head[u]; G[tot].to = v; G[tot].w = w; head[u] = tot; } void dfs(int u){ for(int i = head[u]; i; i = G[i].nxt){ int v = G[i].to; dfs(v); int V = min(siz[u], m); int K = min(siz[v], m); for(int s = V; s >= 1; s--) for(int j = 1; j <= ( u == 0 ? min(s, K) : min(s-1, K) ); j++){ dp[u][s] = max( dp[u][s], dp[u][s - j] + dp[v][j] + G[i].w); } } } void get_size(int u){ siz[u] = 1; for(int i = head[u]; i; i = G[i].nxt){ int v = G[i].to; get_size(v); siz[u] += siz[v]; } } int main(){ freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++){ int fa,w; scanf("%d%d",&fa,&w); add(fa, i, w); } get_size(0); dfs(0); cout<<dp[0][m]<<endl; }
考試突然不會分組背包了,╮(╯▽╰)╭, 一定要記得容量放外面,從大到小啊
18寒假12測