二叉蘋果樹 && 選課(樹上揹包)
阿新 • • 發佈:2020-08-15
二叉蘋果樹:https://ac.nowcoder.com/acm/problem/50505
有一棵二叉蘋果樹,如果數字有分叉,一定是分兩叉,即沒有隻有一個兒子的節點。這棵樹共N個節點,標號1至N,樹根編號一定為1。
我們用一根樹枝兩端連線的節點編號描述一根樹枝的位置。一棵有四根樹枝的蘋果樹,因為樹枝太多了,需要剪枝。但是一些樹枝上長有蘋果,給定需要保留的樹枝數量,求最多能留住多少蘋果。
輸入
5 2 1 3 1 1 4 10 2 3 20 3 5 20
輸出
21
程式碼:
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<sstream> #include<vector> #include<stack> #include<deque> #include<cmath> #include<map> #include<queue> #include<bitset> //#include<hash_map> #define sd(x) scanf("%d",&x) #define lsd(x) scanf("%lld",&x) #define ms(x,y) memset(x,y,sizeof x) #definefu(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define all(a) a.begin(),a.end() #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; //using namespace __gnu_cxx; typedef long long ll; typedef unsigned long long ull; typedef long double ld;const int maxn=1e2+79; const int mod=998244353; const int INF=1e9+7; const double pi=acos(-1); ll dp[maxn][maxn],a[maxn]; vector<od> son[maxn]; int n,k; void dfs(int x,int fa) { for(od y:son[x]) { int nxt=y.to,len=y.len; if(nxt==fa) continue; dfs(nxt,x); //dp:第i個點為根,連著j條根 fd(j,k,1) { fu(t,0,j-1) //左+右組合 dp[x][j]=max(dp[nxt][t]+dp[x][j-t-1]+len,dp[x][j]); } } } int main() { sd(n);sd(k); fu(i,1,n-1) { int u,v,w;sd(u);sd(v);sd(w); son[u].push_back(od{v,w}); son[v].push_back(od{u,w}); } dfs(1,0); int ans=dp[1][k]; printf("%lld\n",ans); return 0; }
選課:https://ac.nowcoder.com/acm/problem/51179
學校實行學分制。每門的必修課都有固定的學分,同時還必須獲得相應的選修課程學分。
學校開設了 N 門的選修課程,每個學生可選課程的數量 M 是給定的。
學生選修了這 M 門課並考核通過就能獲得相應的學分。
在選修課程中,有些課程可以直接選修,有些課程需要一定的基礎知識,必須在選了其他的一些課程的基礎上才能選修。
例如《Windows程式設計》必須在選修了《Windows操作基礎》之後才能選修。
我們稱《Windows操作基礎》是《Windows程式設計》的先修課。
每門課的直接先修課最多隻有一門。
兩門課可能存在相同的先修課。
你的任務是為自己確定一個選課方案,使得你能得到的學分最多,並且必須滿足先修條件。
假定課程之間不存在時間上的衝突。 輸入描述: 輸入檔案的第一行包括兩個整數N、M(中間用一個空格隔開)其中1≤N≤300,1≤M≤N,1≤N≤300,1≤M≤N 接下來N行每行代表一門課,課號依次為1,2,…,N。
每行有兩個數(用一個空格隔開),第一個數為這門課先修課的課號(若不存在先修課則該項為0),第二個數為這門課的學分。
學分是不超過10的正整數。
輸入:
7 4
2 2 0 1 0 4 2 1 7 1 7 6 2 2
輸出:程式碼:
13
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<sstream> #include<vector> #include<stack> #include<deque> #include<cmath> #include<map> #include<queue> #include<bitset> //#include<hash_map> #define sd(x) scanf("%d",&x) #define lsd(x) scanf("%lld",&x) #define ms(x,y) memset(x,y,sizeof x) #define fu(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) #define all(a) a.begin(),a.end() #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 using namespace std; //using namespace __gnu_cxx; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const int maxn=300+79; const int mod=998244353; const ll INF=0x7f7f7f7f; const double pi=acos(-1); ll dp[maxn][maxn],sco[maxn]; vector<int> to[maxn]; int n,m; void dfs(int x) { dp[x][0]=0; if(!to[x].empty()) { for(int y:to[x]) { dfs(y); fd(j,m,0) //總共選j個,01揹包倒序 fu(k,0,m) if(j-k>=0) dp[x][j]=max(dp[x][j],dp[x][k]+dp[y][j-k]); } } if(x!=0)//不是0的話,都要加上x點的學分 fd(i,m,1) dp[x][i]=dp[x][i-1]+sco[x]; } int main() { sd(n);sd(m); fu(i,1,n) { int x,score; sd(x);sd(score); sco[i]=score; to[x].push_back(i); } //建一個超級點0 dfs(0); printf("%lld\n",dp[0][m]); return 0; }
Note:
兩題都是樹上揹包。兩題f[u][j]都是表示以u為根,選j個邊(點)的最優解。
第一題:選邊使邊權和最小。
• f[u][j] = max(f[u][k] + f[v][j – k - 1] + W) • v分別是u的兒子,w為u到v邊上的蘋果數目(也即這條邊權值), k屬於[0, j]。 第二題:選點使點權最大。 • f[u][j] = max(f[u][k] + f[v][j – k ]) • v分別是u的兒子, k屬於[0, j]。 •最後(因為以x為根,點x一定要選上)//把每個點x的學分都加上去,逆序是為了讓一個點的值不被重複加上去 for(int i=m;i>=1;i--) dp[x][i]=dp[x][i-1]+val[x];