**[
数据范围很小,直接开四维 i j k l
分别表示第一次走到(i ,j),第二次走到(k,l)的最大值
如果走到过重复的格子,他们必然需要再减去a((dis[}
最后di。。
#include
using namespace std;
int a[15][15],f[15][15][15][15];
int read()
{
int flag=1,sum=0;
char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-')flag=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return flag*sum;
}
int maxx(int q,int w,int e,int r,int t)
{
return max(q,max(w,max(e,max(r,t))));
}
int main()
{
int n=read();
int x=read(),y=read(),z=read();
while (x&&y&&z)
{
a[x][y]=z;
x=read(),y=read(),z=read();
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
for (int l=1;l<=n;l++)
{
f[i][j][k][l]=maxx(f[i][j][k][l],f[i-1][j][k-1][l],f[i-1][j][k][l-1],f[i][j-1][k-1][l],f[i][j-1][k][l-1])+a[i][j]+a[k][l];
if (i==k&&j==l)
f[i][j][k][l]-=a[i][j];
}
printf("%d",f[n][n][n][n]);
return 0;
}
和方格取数很像,但是多了一个限制条件,来回的路线中不能有同样的格子,那么刚才方格取数的dp就不适用了,我们无法限制他们是否会走到同一个格子
所以分析题目,首先,题目要求找来回两条路径,因为两条路径始终不相交(除起点终点),我们可以都从起点出发走向终点,很容易发现两条路线最终会到达终点的左边和上边,最终交汇于终点处。
我们接着分析,每条路径都是向下或向右走到达终点的,也就是说他们走过的路程(横坐标加纵坐标)一定是相等的,所以我们可以用一维表示两条路径走过的路程,再用两维分别表示两条线段的横坐标。
既f [ k ] [ i ] [ j ]
k 表示两条路径路程
i 表示第一条路径此时的 纵坐标/横坐标
j 表示第二条路径此时的 纵坐标/横坐标
然后就能得出状态转移方程
f[k][i][j]=max(f[k-1][i][j],max(f[k-1][i-1][j],max(f[k-1][i-1][j-1],f[k-1][i][j-1])));
但是我们要回到最开始的问题,这道题的特点就是不能使两条路径相交,于是我们可以在枚举的时候做一点功夫
我们不妨令i这一维表示的是相对在上面的路径,j这一维表示相对在下面的路径,所以对于j 一定有j>i(这样才能保证两条路径不想交)
所以枚举方法如下:
枚举路径路程
····枚举第一条线段横坐标
·········枚举第二条线段横坐标
···············状态转移方程
#include
using namespace std;
int a[55][55],f[105][55][55];
int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-')f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main()
{
int n=read(),m=read();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]=read();
for (int k=3;k<n+m;k++)
{
for (int i=1;i<=min(k,n-1);i++)
{
for (int j=i+1;j<=min(k,n);j++)
{
f[k][i][j]=max(f[k-1][i][j],max(f[k-1][i-1][j],max(f[k-1][i-1][j-1],f[k-1][i][j-1])))+a[i][k-i]+a[j][k-j];
}
}
}
printf("%d",f[m+n-1][n-1][n]);
return 0;
}
树形dp
题目给出中序遍历,然后要确定一颗树,使它的每个节点的加分最大
#include
using namespace std;
int f[35][35],root[35][35];
int dfs(int i,int j)
{
int sum;
if (i>j)return 1;
if (f[i][j]==-1)
{
for (int k=i;k<=j;k++)
{
sum=dfs(i,k-1)*dfs(k+1,j)+f[k][k];
if (sum>f[i][j])
{
f[i][j]=sum;
root[i][j]=k;
}
}
}
return f[i][j];
}
void find(int l,int r)
{
if (l>r)return;
printf("%d ",root[l][r]);
find(l,root[l][r]-1);
find(root[l][r]+1,r);
}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
f[i][j]=-1;
scanf("%d",&f[i][i]);
root[i][i]=i;
}
printf("%d\n",dfs(1,n));
find(1,n);
return 0;
}