• 洛谷 P1006 传纸条 题解


    CSDN同步

    原题链接

    本题 \(n \leq 100\) ,我们先分析一下 大力搜索 的效率。

    众所周知, 从 \(1,1\) 走到 \(n,m\)每次只能向右走或向下走) 的方案数为:

    \[C_{n+m}^m \]

    简单解释:共走 \(n+m\) 步,其中 \(n\) 步向下, \(m\) 步向右。而:

    \[C_{n+m}^m = C_{n+m}^n \]

    组合即可。

    那么,搜索的时间复杂度 至少是 \(O(C_{2 \times n}^n)\).

    大概算一下:

    \[C_{200}^{100}=\frac{200!}{100! \times 100!} \]

    这个数很大,是我们无法接受的。

    所以, 下面考虑\(dp\).

    \(dp_{i,j,k,l}\) 表示 从左上角到右下角经过\(i,j\),再从右下角到左上角经过\(k,l\)的最大路径。

    首先有一个结论: \(dp_{i,j,k,l}=dp_{k,l,i,j}\)

    这是显然的,因为交换两个点值并不会影响答案。

    那么,显然对于 \(i,j\) ,从 \(i,j-1\)\(i-1,j\) 考虑转移。

    对于 \(k,l\) ,从 \(k+1,l\)\(k,l+1\) 考虑转移。

    不难得出:

    \[dp_{i,j,k,l}=\max(dp_{i,j-1,k-1,l},dp_{i-1,j,k-1,l},dp_{i-1,j,k,l-1},dp_{i,j-1,k,l-1})+a_{i,j}+a_{k,l} \]

    也就是从相邻的四个格子加上当前两个格子的值。

    而我们之前的结论是 \(dp_{i,j,k,l}=dp_{k,l,i,j}\)

    又这条路径必然会经过 \((n-1,m)和(n,m-1)\)

    所以答案为

    \[f_{n,m-1,n-1,m} \]

    时间复杂度为\(O(n^2 \times m^2)\),空间复杂度为\(O(n^2 \times m^2)\),可以通过本题。

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    int a[51][51],dp[51][51][51][51];
    int max_four(int a,int b,int c,int d){
    	return max(max(max(a,b),c),d);
    } //求四个数中的最大值
    int main()
    {
      	cin>>n>>m;
      	for(int i=1;i<=n;i++)
      	for(int j=1;j<=m;j++) cin>>a[i][j];
      	for(int i=1;i<=n;i++)
      	for(int j=1;j<=m;j++)
      	for(int k=1;k<=n;k++)
      	for(int l=j+1;l<=m;l++)
    		dp[i][j][k][l]=max_four(dp[i][j-1][k-1][l],dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k][l-1])+a[i][j]+a[k][l];
    	cout<<dp[n][m-1][n-1][m];
      	return 0;
    }
    
    

    下面考虑一个加强:

    如果 \(n,m \leq 100\) 呢?

    这时上述算法的空间与时间都很不理想,有超时和爆空间的可能。

    对于上述的 \(dp_{i,j,k,l}\) ,你会发现:

    \(i+l=j+k\)

    这就是 走的步数。

    那么,我们只需让 \(step=i+l=j+k\)

    然后用 \(dp_{step,i,j}\) 进行转移即可。把上面的状态转移方程中的 \(k\) 改为 \(step-j\)\(l\) 改为 \(step-i\). 所以:

    \[dp_{step,i,j}=\max(dp_{step-1,i,j},dp_{step-1,i-1,j-1},dp_{step-1,i,j-1},dp_{step-1,i-1,j})+a_{i,step-i+1}+a_{j,step-j+1} \]

    时间复杂度为 \(O(nm \times (n+m))\),空间复杂度为 \(O(nm \times (n+m))\).

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    int dp[101][51][51],a[51][51];
    //因为step最大会达到2*n-1,所以数组要开大
    int max_four(int a,int b,int c,int d){
    	return max(max(max(a,b),c),d);
    } //求四个数中的最大值
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=m;j++) cin>>a[i][j];
    	for(int step=1;step<=n+m-1;step++)
    	for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++){
    		if(step-i+1<1 || step-j+1<1) continue; //去掉k和l是负数的情况
    		dp[step][i][j]=max_four(dp[step-1][i][j],dp[step-1][i-1][j-1],dp[step-1][i][j-1],dp[step-1][i-1][j])+a[i][step-i+1]+a[j][step-j+1];
    		if(i==j) dp[step][i][j]-=a[i][step-i+1]; //如果i=j,说明两点重合,需要减掉一个
    	}
    	cout<<dp[n+m-1][n][n];
    	return 0;
    }
    
    
    
    
    简易的代码胜过复杂的说教。
  • 相关阅读:
    五、批量插入
    四、操作BLOB类型字段
    三、使用PreparedStatement实现CRUD操作
    二、获取数据库连接
    一、JDBC概述
    最短平均码长(挑出假硬币问题的解法)
    信息量和信息熵
    洛谷P2114
    Servlet续(HttpServletRequest类和HttpServletResponse类)
    Servlet
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12498861.html
Copyright © 2020-2023  润新知