树的最长路径
题目链接:树的最长路径 分析:我们需要在树上DP,状态表示这一方面是比较困难的,我们要找最长的一条路径,那我们随意画一棵树 代码实现:
#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;
for(int i=h[u];i;i=ne[i]){
int j=e[i];
if(j==father) continue;
int d=dfs(j,u)+w[i];
if(d>=d1){
d2=d1;
d1=d;
}
else if(d>d2){
d2=d;
}
}
ans=max(ans,d1+d2);
return d1;
}
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(b,a,c);
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}
树的中心
题目链接:树的中心 分析:这是一道很好的树形DP题,我们可以随便选取一个点,向上遍历所有点,再向下遍历所有点,遍历的途中更新答案,详细讲解见代码。 代码实现: 代码中的细节还是比较多的
#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];
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){
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){
for(int i=h[u];i;i=ne[i]){
int j=e[i];
if(j==father) continue;
if(p[u]==j) up[j]=max(d2[u],up[u])+w[i];
else up[j]=max(up[u],d1[u])+w[i];
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);
dfs_u(1,0);
int res=d1[1];
for(int i=2;i<=n;i++){
res=min(res,max(up[i],d1[i]));
}
cout<<res<<endl;
return 0;
}
数字转换
题目链接:数字转换 分析:这道题还是比较有意思的,我们把所有能连的边都预处理出来并连上,然后就需要求最长的一条路径,回到了第一题。需要注意的是,本题可能会出现多棵树的情况,即不一定所有点都连通,因此我们对每个点都搜一遍。 代码实现:
#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 ;
st[u]=1;
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++){
for(int j=2;j<=n/i;j++){
sum[i*j]+=i;
}
}
for(int i=2;i<=n;i++){
if(sum[i]<i){
add(sum[i],i);
}
}
for(int i=1;i<=n;i++){
if(!st[i])
dfs(i);
}
cout<<res;
return 0;
}
二叉苹果树
题目链接:二叉苹果树 分析:这道题还是比较easy的,感觉到一股01背包味?再品品,感觉到一股区间DP味?或许这就是算法的美妙之处吧(你中有我,我中有你)。 代码实现:
#include<iostream>
using namespace std;
int n,q;
const int N=110,M=2*N;
int dp[N][N];
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--){
for(int k=0;k<j;k++){
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);
add(b,a,c);
}
dfs(1,0);
cout<<dp[1][q];
return 0;
}
战略游戏
题目链接:战略游戏 分析:一股子状态机模型味,就按照那个模型写吧,详见代码。 代码实现:
#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];
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];
dp[u][1]+=min(dp[j][0],dp[j][1]);
}
}
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;
}
}
int root=0;
while(vis[root]) root++;
dfs(root);
printf("%d\n",min(dp[root][0],dp[root][1]));
}
return 0;
}
皇宫看守
题目链接:皇宫看守 分析:注意,本题和上题不一样,上题是观察边,本题是观察点,但其实思路都差不多,详见代码。 代码实现:
#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];
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]);
f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);
sum += min(f[j][1], f[j][2]);
}
f[u][1] = 1e9;
for (int i = h[u]; ~i; i = ne[i]){
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;
}
}
int root = 1;
while (st[root]) root ++ ;
dfs(root);
cout << min(f[root][1], f[root][2]) << endl;
return 0;
}
树形DP的题感觉还都是很有意思的,还要在洛谷上多刷刷题!
|