DP入门到精通系列
学到这,树形dp,和记忆化这些其实都很简单,且是很常见的思路。那么话不多说直接开始dp之旅吧。
没有上司的舞会
不管三七二十一,dp问题是一定要去思考状态表示的,而这个一般都是从题目里面就会给到你
没有职员愿意和上司一起参会(太真实了),用算法语言来说就是你选了这个就不能选那个,也就是说,如果你选了父节点,就不可以选子节点,,所有就有 f[ u , 0 ],f[ u , 1 ]表示你选与不选这第u个结点,其实没有职员愿意和上司一起参会这句话还顺路把状态计算给了出来
因为如果你选了这个结点f[ u , 1 ],那么你就不能选子节点,(假设s是子节点)所以必然是f[ u ][ 1 ]+=f[ s ][ 0 ],如果你没选这个f[ u , 0 ]那么就可以在子节点中抉择选与不选,f[ u ][ 0 ]+=max(f[ j ][ 0 ],f[ j ][ 1 ])。
const int N =6010;
int n;
int f[N][2];
int e[N],ne[N],happy[N],h[N],idx=0;
void add(int a,int b)//邻接表存图
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool has_father[N];
void dfs(int u)
{
f[u][1]=happy[u];
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
dfs(j);
f[u][0]+=max(f[j][0],f[j][1]);
f[u][1]+=f[j][0];
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>happy[i];
memset(h,-1,sizeof h);
for(int i=0;i<n-1;i++)
{
int a,b;
cin>>a>>b;
add(b,a);
has_father[a]=true;
}
int root=1;
while(has_father[root++]);//找到根节点
root-=1;
dfs(root);
cout<<max(f[root][0],f[root][1]);
return 0;
}
滑雪
滑雪问题大家应该不陌生,但面对dp问题我们还是先老套路,状态表示,状态计算,我们可以这么表示,f [ i , j ]表示从i j 出发的最大路径是多少,然后 f [ i , j ]他可以往左往右,向上向下去滑,那么f [ i , j ]的值就是朝这四个方向滑的最大值。所以我们也顺便把状态计算写了出来
f[ i ][ j ] =max(f[ i ][ j ],(上下左右)+1);
/*
输入格式
第一行包含两个整数 R 和 C。
接下来 R 行,每行包含 C 个整数,表示完整的二维矩阵。
输出格式
输出一个整数,表示可完成的最长滑雪长度。
*/
const int N = 310;
int f[N][N];
int h[N][N];
int n,m;
int dirx[4]={0,1,0,-1};
int diry[4]={1,0,-1,0};
int dp(int x,int y)
{
if(f[x][y]!=-1)return f[x][y];
f[x][y]=1;
for(int i=0;i<4;i++)
{
int dx=dirx[i]+x;
int dy=diry[i]+y;
if(dx>=1&&dx<=n&&dy>=1&&dy<=m&&h[dx][dy]<h[x][y])
{
f[x][y]=max(f[x][y],dp(dx,dy)+1);
}
}
return f[x][y];
}
int main()
{
memset(f,-1,sizeof f);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cin>>h[i][j];
}
int res=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
res=max(res,dp(i,j));
}
cout<<res;
return 0;
}
DP问题入门到精通结束
不知不觉也写了挺多的了,我也从中学到了很多,教学相长确实如此,虽然我也不至于有“教”这个境界吧
更多的是想我的思路,学习心得能够帮到别人,所以还是那句话,希望能让看完的你有所收获吧哈哈哈。