The longest path of a tree
Title link: The longest path of a tree
Analysis: We need DP on the tree, it is difficult to represent the state. We want to find the longest path, so we draw a tree at will
Code implementation:
#include<iostream> using namespace std; const int N=1e4+10,M=2*N; int n,h[N],w[M],ne[M],idx=1,e[M],ans; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } int dfs(int u,int father){ int d1=0,d2=0;//d1 records the longest edge, d2 records the second longest edge for(int i=h[u];i;i=ne[i]){//Traverse all outbound edges int j=e[i]; if(j==father) continue;//If it's his father, skip it, and stop searching here int d=dfs(j,u)+w[i];//Find the longest plus w[i] in the subtree with j as the root node if(d>=d1){//Update Longest Edge and Second Longest Edge d2=d1; d1=d; } else if(d>d2){//Update Second Long Edge d2=d; } } ans=max(ans,d1+d2);//Update Answer return d1;//Return to Longest Edge } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c);//Add two edges add(b,a,c); } dfs(1,0);//The second parameter passes in an unused node at will, and the first node is at will, because it can be converted into a tree regardless of which node it starts from. printf("%d\n",ans); return 0; }
Center of Tree
Title link: Center of Tree
Analysis: This is a good tree DP question, we can pick a point at will, walk all points up, then all points down, update the answers and explain the code in detail.
Code implementation:
More details in the code
#include<iostream> using namespace std; const int N=1e4+10,M=2*N,INF=1e9; int n,h[N],e[M],ne[M],w[M],idx=1; bool is_leaf[N]; int d1[N],d2[N],p[N],up[N];//d1[i] denotes the longest downward path from point i, d2 the second longest, p the point through which the longest downward path passes (record the nearest point), and up the longest upward path void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } int dfs_d(int u,int father){//Search down on topics similar to those above d1[u]=d2[u]=0; for(int i=h[u];i;i=ne[i]){ int j=e[i]; if(j==father) continue; int d=dfs_d(j,u)+w[i]; if(d>=d1[u]){ d2[u]=d1[u]; d1[u]=d; p[u]=j; } else if(d>=d2[u]) d2[u]=d; } return d1[u]; } int dfs_u(int u,int father){//Go up for(int i=h[u];i;i=ne[i]){ int j=e[i]; if(j==father) continue; //Here, when u is 1, up[u]=0, but this does not affect the transfer, because it must take the longest downward path and turn to the second longest. if(p[u]==j) up[j]=max(d2[u],up[u])+w[i];//If the longest way down from u passes j, then the longest way up from point J is to go up or turn to the second longest way down else up[j]=max(up[u],d1[u])+w[i];//Otherwise, just choose one from the turn to the longest downward and upward dfs_u(j,u); } } int main(){ int n; scanf("%d",&n); for(int i=1;i<n;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } dfs_d(1,0);//down dfs_u(1,0);//Up int res=d1[1];//Note that traversing all points of Node 1 in this topic is equivalent to walking down for(int i=2;i<=n;i++){//Other points need to take the maximum value from the maximum up and the minimum value from the maximum down and the minimum value from the res res=min(res,max(up[i],d1[i])); } cout<<res<<endl; return 0; }
Digital Conversion
Title link: Digital Conversion
Analysis: This question is still interesting. We preprocess and connect all the edges that can be connected, then we need to find the longest path to get back to the first question. It is important to note that there may be multiple trees in this topic, that is, not all points are connected, so we will search through each point.
Code implementation:
#include<iostream> using namespace std; const int N=5e4+10; int idx=1,h[N],ne[N],e[N],sum[N]; int d1[N],d2[N],res; bool st[N]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dfs(int u){ if(d1[u]) return ;//Return directly to the longest if searched st[u]=1;//Mark it after searching for(int i=h[u];i;i=ne[i]){ int j=e[i]; dfs(j); if(d1[j]+1>d1[u]) d2[u]=d1[u],d1[u]=d1[j]+1; else if(d1[j]+1>d2[u]) d2[u]=d1[j]+1; } res=max(res,d1[u]+d2[u]); } int main(){ int n;cin>>n; for(int i=1;i<=n;i++){//First deal with the sum of all the divisors for(int j=2;j<=n/i;j++){//Since the approximate number does not contain itself, all JS start from 2 sum[i*j]+=i; } } for(int i=2;i<=n;i++){//Add an edge from 2, otherwise add an edge from 0 to 1 if(sum[i]<i){ add(sum[i],i);//We don't need to search backwards, so we can create an edge from the smallest tree to the largest one. } } for(int i=1;i<=n;i++){//Search from the smallest point if(!st[i])//If not searched dfs(i); } cout<<res; return 0; }
Binary apple tree
Title link: Binary apple tree
Analysis: This question or easy, feel a 01 backpack smell? Re-taste, feel a smell of DP? Perhaps that's the beauty of algorithms (you have me, I have you).
Code implementation:
#include<iostream> using namespace std; int n,q; const int N=110,M=2*N; int dp[N][N];//dp[i][j] denotes a subtree with I as its root node, retaining the maximum number of apples remaining in the j-root branch int h[N],ne[M],w[M],idx=1,e[M]; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } void dfs(int u,int father){ for(int i=h[u];i;i=ne[i]){ if(e[i]==father) continue; dfs(e[i],u); for(int j=q;j;j--){//Enumerate the number of branches reserved with u as the root node for(int k=0;k<j;k++){//Enumerates the number of branches reserved for a subtree with this child node as the root node dp[u][j]=max(dp[u][j],dp[e[i]][k]+dp[u][j-k-1]+w[i]); } } } } int main(){ cin>>n>>q; for(int i=1;i<n;i++){ int a,b,c; cin>>a>>b>>c; add(a,b,c);//Build two-way sides add(b,a,c); } dfs(1,0); cout<<dp[1][q]; return 0; }
Strategy Game
Title link: Strategy Game
Analysis: A stream of state machine model flavor, write according to that model, see the code for details.
Code implementation:
#include<iostream> #include<cstring> using namespace std; const int N=1510; int n; int h[N],e[N],ne[N],idx=1; int dp[N][2];//dp[i][0] denotes the minimum value when left at I and when placed at 1 bool vis[N]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dfs(int u){ dp[u][0]=0,dp[u][1]=1; for(int i=h[u];i;i=ne[i]){ int j=e[i]; dfs(j); dp[u][0]+=dp[j][1];//If u-node is not placed, child nodes must be placed dp[u][1]+=min(dp[j][0],dp[j][1]);//If u-node is placed, child nodes can be left alone } } int main(){ while(scanf("%d",&n)!=-1){ memset(h,0,sizeof h); idx=1; memset(vis,0,sizeof vis); for(int i=1;i<=n;i++){ int a,b; scanf("%d:(%d)",&a,&b); while(b--){ int c; scanf("%d",&c); add(a,c); vis[c]=true;//Mark it as not the root node } } int root=0; while(vis[root]) root++;//Find the root node dfs(root); printf("%d\n",min(dp[root][0],dp[root][1])); } return 0; }
Palace guards
Title link: Palace guards
Analysis: Note that this topic is different from the previous one. The previous one is the observation edge, and this one is the observation point. In fact, the ideas are similar, see the code for details.
Code implementation:
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 1510; int n; int h[N], w[N], e[N], ne[N], idx; int f[N][3];//f[i][0] means point I is observed by its parent node, f[i][1] means watched by its child node, and f[i][2] means watched by itself bool st[N]; void add(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; } void dfs(int u){ f[u][2] = w[u]; int sum = 0; for (int i = h[u]; ~i; i = ne[i]){ int j = e[i]; dfs(j); f[u][0] += min(f[j][1], f[j][2]);//If this node is watched by a parent node, its children must look for a son or look at it themselves f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);//If this node looks at itself, its children are free sum += min(f[j][1], f[j][2]);//sum is used when f[u][1], in which case all the child nodes have to depend on themselves or their sons to add up } f[u][1] = 1e9;//For f[u][1], we want to select the best child node, so initialize to a large number and then take the minimum case value for (int i = h[u]; ~i; i = ne[i]){//Enumerate all the child nodes, which are guarded, then the total price is minus min(f[j][1], f[j][2]) plus f[j][2] int j = e[i]; f[u][1] = min(f[u][1], sum - min(f[j][1], f[j][2]) + f[j][2]); } } int main(){ cin >> n; memset(h, -1, sizeof h); for (int i = 1; i <= n; i ++ ){ int id, cost, cnt; cin >> id >> cost >> cnt; w[id] = cost; while (cnt -- ){ int ver; cin >> ver; add(id, ver); st[ver] = true;//Under non-root node tag } } int root = 1; while (st[root]) root ++ ;//Find the root node dfs(root); cout << min(f[root][1], f[root][2]) << endl; return 0; }
Tree DP's Title feels interesting, but also brush more on the Logu!