Description
热爱电子娱乐的同学们对于技能树一定不陌生.就是说,要先学习低级的垃圾技能,特定
的几个垃圾技能学会了,才能学习更强的技能.比如说,要先学火球术和烈火墙,才能学习地狱
烈焰.科技树也是一样.要先研究出电力和内燃机,才能研究工业学.那么,现在我们把问题简化,
这是一个技能树(或者科技树).格子上的数,是威力值.要先学会第一排第二个和第三个,才能
学会第二排的第二个.每个技能学习的前提都是左上和右上的两个技能.假设现在有一个第一
层有N 个技能的技能树,而且技能点是有限的,只能学习M 个技能,我们想知道最大的威力值
之和是多少.
(ps:这个不是样例)Input Format
第一行两个数N 和M,如题所述
之后N 行,第i 行,有n+1-i 个数.表示一个技能树.Output Format
输出一个数,表示最大威力值之和
Sample Input
4 5
1 1 1 1
1 2 1
1 1
1Sample Output
6
Data Limit
对于40%的数据,N<=10
对于100%的数据,N<=50,M<=500,所有数据都在int 之内.
技能树上的dp是一种不容易想到的dp问题,因为这种树状结构的dp大家都很熟悉,拿到这道题很容易就想成了树规或者从上向下,从下向上之类的dp问题,但是看到这道题的数据范围又是这么小,一看就有问题,然后就卡住了,最后只能写一个
那么现在我们仔细看看一下这道题,因为dp要无后效性(当前的状态只与前一个状态有关,而再前面的状态影响不了当前的状态,当前的状态也只能影响它后面的状态,不能影响再后面的状态),而如果我们从上向下dp的话,能不能去某一个数必须要看它上面的伞状结构的那一部分取没有,完全没法正常dp,方程一直列不出来。
我们就开始考虑,如何是我们的dp达到无后效性呢?
仔细观察我们可以发现无论我们怎么取,轮廓线都是一条锯齿状的折线。
我们发现每一列只有一个点在轮廓线上,而这些点,就是我们可以用来dp的点。
我们考虑从左向右dp,每一个在轮廓线上面的点只能有它前一列的上面或者下面得来,符合dp的无后效性
如上图,我们设“14”在第一行第一列,“33”在第二行第二列密码“15”在第一行第三列
而如果取一个点,也就说明要取它上面的所有点,我们可以先预处理将每个点上面点的数量存在num数组,价值和存在sum数组,f[k][i][j]表示[i][j]轮廓线取到这个坐标,一共取了k个数的最大值,然后开始状态转移!
然后还有一个情况是需要注的,就是可能轮廓线不是连续的,前面一段,后面一段,这时我们要处理第0行的情况,第0行dp时状态就由它前面一个0行的和它上面前一个第1行dp过来,即:
大概就是这样了,然后我的程序在下面,但是,一直wa一个点,实在是改不出来了_(:зゝ∠),还是发上来作为参考吧。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define M 105
using namespace std;
int a[M][M],sum[M][M],num[M][M],f[M*5][M][M],n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=i;j<=2*n-i;j+=2)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=2*n-1;j++)
{
sum[i][j]=sum[i-2][j]+a[i][j];
num[i][j]=num[i-1][j];
if(a[i][j])num[i][j]++;
}
for(int i=1;i<=n*2-1;i+=2)
{
f[1][1][i]=a[1][i];
f[1][0][i+1]=max(f[1][0][i-1],f[1][1][i]);
}
for(int k=2;k<=m;k++)
for(int j=1;j<=2*n-1;j++)
for(int i=(j%2==0?0:1);i<=n-abs(n-j);i+=2)
{
if(i==0)
{
f[k][i][j]=max(f[k][0][j-2],f[k][1][j-1]);
continue;
}
if(k-num[i][j]<0||(f[k-num[i][j]][i-1][j-1]==0&&f[k-num[i][j]][i+1][j-1]==0))continue;
f[k][i][j]=max(f[k-num[i][j]][i-1][j-1],f[k-num[i][j]][i+1][j-1])+sum[i][j];
}
int ans=0;
for(int i=1;i<=2*n-1;i++)
ans=max(ans,f[m][1][i]);
cout<<ans;
return 0;
}
如果有什么问题,或错误,请在评论区提出,谢谢。
来源:CSDN
作者:anantheparty
链接:https://blog.csdn.net/u011327397/article/details/52119899