P1122 最大子树和
思路
d
p
[
i
]
dp[i]
dp[i]表示以
i
i
i为根节点的最大子树和
对所有
i
i
i的子结点
k
k
k,如果
d
p
[
k
]
>
0
dp[k]>0
dp[k]>0则
d
p
[
i
]
+
=
d
p
[
k
]
dp[i]+=dp[k]
dp[i]+=dp[k],如果
d
p
[
k
]
<
=
0
dp[k]<=0
dp[k]<=0则舍弃掉这一枝
实现
#include <bits/stdc++.h>
#define maxn 16010
using namespace std;
typedef long long ll;
int ans = -2147483647,n;
int dp[maxn];
vector<int> G[maxn];
void dfs(int child,int fa)
{
int sum = G[child].size();
for(int i=0;i<sum;i++)
{
if(G[child][i]!=fa)
{
dfs(G[child][i],child);
if(dp[G[child][i]]>0) dp[child]+=dp[G[child][i]];
}
}
}
int main()
{
int t1,t2;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&dp[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d %d",&t1,&t2);
G[t1].push_back(t2);
G[t2].push_back(t1);
}
// for(int i=1;i<=n;i++)
// {
// for(int j=0;j<G[i].size();j++)
// {
// cout<<G[i][j]<<" ";
// }
// cout<<endl;
// }
dfs(1,0);
for(int i=1;i<=n;i++)
{
//cout<<dp[i]<<endl;
ans = max(ans,dp[i]);
}
cout<<ans;
return 0;
}
P1273 有线电视网
自己并没有做出来
参考题解
P1352 没有上司的舞会
思路
树形dp,用 d p 1 [ i ] dp1[i] dp1[i]表示以i为根且 i i i不参加的子树快乐指数最大值,用 d p 2 [ i ] dp2[i] dp2[i]表示以i为根且 i i i参加的子树快乐指数最大值。
实现
#include <bits/stdc++.h>
#define maxn 6005
using namespace std;
typedef long long ll;
int ans,n;
int dp1[maxn];
int dp2[maxn];
vector<int> G[maxn];
int a[maxn];
bool flag[maxn];
int root;
void dp(int x)
{
dp2[x] = a[x];
for(int i=0;i<G[x].size();i++)
{
dp(G[x][i]);
dp1[x] += max(dp1[G[x][i]],dp2[G[x][i]]);
dp2[x] += dp1[G[x][i]];
}
}
int main()
{
int t1,t2;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&t1,&t2);
G[t2].push_back(t1);
flag[t1] = 1;
}
for(int i=1;i<=n;i++)
{
if(!flag[i]) root = i;
}
dp(root);
cout<<max(dp1[root],dp2[root]);
return 0;
}
P2015 二叉苹果树
思路
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示留了
i
i
i号节点和
i
i
i下方的
j
j
j条边时最多可保留的苹果数
枚举
k
k
k,
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
s
o
n
]
[
k
]
+
d
p
[
i
]
[
j
−
k
−
1
]
+
w
[
i
]
[
s
o
n
]
)
dp[ i ][ j ] = max(dp[ i ][ j ] , dp[ son ][ k ] + dp[ i ][ j-k-1 ] + w[ i ][ son ])
dp[i][j]=max(dp[i][j],dp[son][k]+dp[i][j−k−1]+w[i][son])
实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int son[maxn][maxn],f[maxn][maxn];
int n,m,w[maxn][maxn],cnt[maxn],vis[maxn];
void dfs(int k)
{
vis[k]=1;
for(int i=1;i<=cnt[k];i++)
{
int ny=son[k][i];
if(vis[ny]==1)continue;
vis[ny]=1;
dfs(ny);
for(int j=m;j>=1;j--)
for(int g=j-1;g>=0;g--)
{
f[k][j]=max(f[k][j],f[ny][g]+f[k][j-g-1]+w[k][ny]);
}
}
return;
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
w[x][y]=w[y][x]=z;
son[x][++cnt[x]]=y;
son[y][++cnt[y]]=x;
}
dfs(1);
cout<<f[1][m]<<endl;
}
P2014 [CTSC1997] 选课
思路
和P2015类似,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示以
i
i
i号节点为根且共保留了包括
i
i
i在内的
j
j
j个节点时最多拥有的学分
枚举
k
k
k,
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
s
o
n
]
[
k
]
+
d
p
[
i
]
[
j
−
k
]
)
dp[ i ][ j ] = max(dp[ i ][ j ] , dp[ son ][ k ] + dp[ i ][ j-k ] )
dp[i][j]=max(dp[i][j],dp[son][k]+dp[i][j−k])
- 实现时将0也看成一个节点即可让整个图变成一棵树
- 需要给m加一
实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 305
vector<int> G[maxn];
int w[maxn];
int n,m;
int f[maxn][maxn];
bool flag[maxn];
void dfs(int k)
{
for(int i=0;i<G[k].size();i++)
{
int ny=G[k][i];
dfs(ny);
for(int j=m;j>0;j--)
for(int g=1;g<j;g++)
{
f[k][j]=max(f[k][j],f[ny][g]+f[k][j-g]);
}
}
return;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x,y,z;
scanf("%d%d",&x,&y);
f[i][1]=y;
G[x].push_back(i);
}
++m;
dfs(0);
cout<<f[0][m]<<endl;
}
P1613 跑路
思路
f
[
i
]
[
j
]
[
k
]
=
1
f[i][j][k]=1
f[i][j][k]=1表示
i
i
i和
j
j
j之间有一条长度为
2
k
2^k
2k的路径,初始时对于所有连通的
i
i
i和
j
j
j,
f
[
i
]
[
j
]
[
0
]
=
1
,
f
[
i
]
[
j
]
[
1
]
=
1
f[i][j][0]=1,f[i][j][1]=1
f[i][j][0]=1,f[i][j][1]=1当且仅当存在
k
k
k使得
f
[
i
]
[
k
]
[
0
]
=
=
1
f[i][k][0]==1
f[i][k][0]==1 且
f
[
k
]
[
j
]
[
0
]
=
=
1
f[k][j][0]==1
f[k][j][0]==1。
最优解路径长度<=maxlongint,所以处理到
f
[
i
]
[
j
]
[
64
]
f[i][j][64]
f[i][j][64]即可,若
i
i
i和
j
j
j之间有一条长度为
2
k
2^k
2k的路径,则将
d
i
s
[
i
]
[
j
]
dis[i][j]
dis[i][j]设为1,最后跑一个最短路。
实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 305
const int N=55;
const int K=70;
int f[N][N][K],dis[N][N],n,m;
int main()
{
int s,t;
cin>>n>>m;
memset(dis,0x3f,sizeof dis);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&s,&t);
f[s][t][0]=1;
dis[s][t]=1;
}
for(int o=1;o<=64;o++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
if(f[j][i][o-1]==true and f[i][k][o-1]==true)
{
f[j][k][o]=true;
dis[j][k]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);
printf("%d",dis[1][n]);
return 0;
}
P1040 [NOIP2003 提高组] 加分二叉树
思路
f
[
i
]
[
j
]
f[i][j]
f[i][j]为节点
i
i
i到
j
j
j的最大得分,答案即为
f
[
1
]
[
n
]
f[1][n]
f[1][n]。对于二叉树的中序遍历,任意选定一个点,左边的就是左子树,右边的就是右子树,可以很方便的做区间dp。
枚举
k
k
k:
f
[
i
]
[
j
]
=
f
[
i
]
[
k
−
1
]
∗
f
[
k
+
1
]
[
j
]
+
f
[
k
]
[
k
]
f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k]
f[i][j]=f[i][k−1]∗f[k+1][j]+f[k][k]
使得
f
[
i
]
[
j
]
f[i][j]
f[i][j]最大的
k
k
k值保存下来,
r
o
o
t
[
i
]
[
j
]
=
k
root[i][j]=k
root[i][j]=k
递归输出区间[l,r]的前序序列:
r
o
o
t
[
l
,
r
]
root[l,r]
root[l,r],区间
[
l
,
r
o
o
t
[
l
,
r
]
−
1
]
[l,root[l,r]-1]
[l,root[l,r]−1]的前序序列,区间
[
r
o
o
t
[
l
,
r
]
+
1
,
r
]
[root[l,r]+1,r]
[root[l,r]+1,r]的前序序列
实现
来自 洛谷题解区
#include<iostream>
#include<cstdio>
using namespace std;
int n,v[39],f[47][47],i,j,k,root[49][49];
void print(int l,int r){
if(l>r)return;
if(l==r){printf("%d ",l);return;}
printf("%d ",root[l][r]);
print(l,root[l][r]-1);
print(root[l][r]+1,r);
}
int main() {
scanf("%d",&n);
for( i=1; i<=n; i++) scanf("%d",&v[i]);
for(i=1; i<=n; i++) {f[i][i]=v[i];f[i][i-1]=1;}
for(i=n; i>=1; i--)
for(j=i+1; j<=n; j++)
for(k=i; k<=j; k++) {
if(f[i][j]<(f[i][k-1]*f[k+1][j]+f[k][k])) {
f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];
root[i][j]=k;
}
}
printf("%d\n",f[1][n]);
print(1,n);
return 0;
}
欢迎指正-