【问题标题】:Top Down Approach for this dynamic programming problem此动态规划问题的自顶向下方法
【发布时间】:2020-05-29 05:37:11
【问题描述】:

问题来了-

给定大小为 n 的数组 B。您必须构造数组 A 使得 1

示例 B=[1,2,3] A 可以是 [1,2,1],[1,1,3],[1,2,3] 在所有这些情况下,成本都是 2,这是最大值.

约束 n

这是我的方法 - 当 A[i]=1 或 A[i]=B[i]

时成本最大

所以我创建了大小为 [100002][2][102] 的 dp[idx][flag][curr],它计算直到索引 idx 的成本。 flag 将为 0 或 1,表示 A[i] 应分别为 1 或 B[i]。 curr 将是 A[i] 的值,具体取决于标志

这是我的代码

     #include<bits/stdc++.h>
using namespace std;
#define boost ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
typedef long long int ll;
#define mod 1000000007

 ll n;
ll dp[100002][2][101];
ll b[100005];
 ll solve(ll idx,ll flag,ll curr)
 {

     if(idx>=n)
         return 0;
     ll s1=0;
    if(dp[idx][flag][curr]!=-1)
        return dp[idx][flag][curr];
     if(idx==0)
     {
         int left=solve(idx+1,0,curr);
         int right=solve(idx+1,1,curr);
         return dp[idx][flag][curr]=max(left,right);
     }
     else
     {
         if(flag==0)
         {
             s1=abs(curr-1);
             return dp[idx][flag][curr]=s1+max(solve(idx+1,0,1),solve(idx+1,1,1));
         } 
         else
         {
             s1=abs(b[idx]-curr);
        return dp[idx][flag][curr]=s1+max(solve(idx+1,0,b[idx]),solve(idx+1,1,b[idx]));
         }
     }
 }


int main()
{
    boost
ll t;
cin>>t;
while(t--)
{ 
cin>>n;
memset(dp,-1,sizeof(dp));
ll res=0;
for(int i=0;i<n;i++)
    cin>>b[i];
ll s1=solve(0,0,1);//Starting from idx 0 flag 0 and value as 1
ll s2=solve(0,1,b[0]);//Starting from idx 0 flag 1 and value as B[0]
cout<<max(s1,s2)<<"\n";
}
}'

有没有办法减少 dp 的状态或任何其他自上而下的解决方案,因为如果 B[i] 的值很大,我的代码会失败

【问题讨论】:

  • 请不要使用“竞争性”网站作为学习或教学资源,因为它们只会教给你真正的坏习惯,永远不会让你就业(或者任何人想用十-脚杆,真的)。获取a few good books,参加课程,至少学习 C++ 的基础知识,以及一些常见和标准的算法和数据结构。训练、实验、编程、学习。然后,您可以使用诸如培训资源之类的网站。
  • 我只是在练习。我猜它对编码面试很有好处
  • 此类网站(以及您在此处展示的)所提倡的风格的代码适合编码面试。恰恰相反。这就是为什么你不应该使用这样的网站作为初学者来学习编程。

标签: c++ c++14 dynamic-programming


【解决方案1】:

您实现了一种递归方法。在这里,一个简单的迭代实现允许获得 O(n) 的时间效率和 O(1) 的空间效率 (不计算输入数组所需的空间)。

您正确地指出,在索引i,我们只有两个选择,a[i]=1(标志 = 0)或a[i]=b[i](标志 = 1)

基本思想是,当研究在索引i 处做出什么选择时,我们只需要知道在索引i-1 处结束的最佳和是多少,对于 flag = 0 (sum0) 或 flag = 1 (sum1)。

我们不需要显式计算数组a[.]

注意:我在您的代码中保留了long long int,但似乎int 在这里已经足够了。

#include    <iostream>
#include    <cstdio>
#include    <vector>
#include    <cstdlib>
#include    <algorithm>

#define mod 1000000007          // needed ???

long long int sum_diff (const std::vector<long long> &b) {
    int n = b.size();
    long long int sum0 = 0;
    long long int sum1 = 0;
    for (int i = 1; i < n; ++i) {
        long long int temp = std::max (sum0, sum1 + b[i-1] - 1);            // flag = 0: a[i] = 1
        sum1 = std::max (sum0 + b[i] - 1, sum1 + std::abs(b[i] - b[i-1]));  // flag = 1: a[i] = b[i]
        sum0 = temp;
    }
    return std::max (sum0, sum1);
 }


int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int t;
    std::cin >> t;
    while(t--) { 
        int n;
        std::cin >> n;
        std::vector<long long int> b(n);
        for(int i = 0;i < n; i++) std::cin >> b[i];
        long long int s = sum_diff (b);
        std::cout << s << "\n";
    }
}

由于您坚持采用自上而下(递归)的方法,我在以下代码中实现了这两种方法。但我坚持认为在这种情况下迭代解决方案更好。

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <vector>
#include    <algorithm>

int sum_diff (const std::vector<int> &b) {
    int n = b.size();
    int sum0 = 0;
    int sum1 = 0;
    for (int i = 1; i < n; ++i) {
        int temp = std::max (sum0, sum1 + b[i-1] - 1);                      // flag = 0: a[i] = 1
        sum1 = std::max (sum0 + b[i] - 1, sum1 + std::abs(b[i] - b[i-1]));  // flag = 1: a[i] = b[i]
        sum0 = temp;
    }
    return std::max (sum0, sum1);
 }

 void sum_diff_recurs (const std::vector<int> &b, int i, int&sum0, int &sum1) {
    if (i == 0) {
        sum0 = sum1 = 0;
        return;
    }
    sum_diff_recurs (b, i-1, sum0, sum1);
    int temp = std::max (sum0, sum1 + b[i-1] - 1);                      // flag = 0: a[i] = 1
    sum1 = std::max (sum0 + b[i] - 1, sum1 + std::abs(b[i] - b[i-1]));  // flag = 1: a[i] = b[i]
    sum0 = temp;
 }

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int t;
    std::cin >> t;
    while(t--) { 
        int n, sum0, sum1;
        std::cin >> n;
        std::vector<int> b(n);
        for(int i = 0; i < n; i++) std::cin >> b[i];
        int s = sum_diff (b);
        std::cout << s << "\n";
        sum_diff_recurs (b, n-1, sum0, sum1);
        std::cout << std::max(sum0, sum1) << "\n";
    }
}

【讨论】:

  • 我知道这可以通过自下而上的方法来完成。但是您能否更改我的递归解决方案中的任何内容或提供另一种有用的自上而下的方法。因为一开始很难考虑迭代实现
  • 请注意,该算法仍然是递归的:我从索引i-1 解决方案获得索引i 解决方案。等效的递归实现(自上而下)是立即的。
  • 请看一下新代码。两种方法都实现了。
  • 好的,这很有帮助。我会接受你的回答。你能想出任何方法来改变我的自上而下的方法,使它不依赖于任何一个州吗?或者不可能
【解决方案2】:

其实我只用了idx和flag这两个状态就找到了解决办法

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;

ll n,k;
ll dp[100002][2];
ll b[100005];

ll solve(ll idx,ll flag)
{
    if(idx>=n-1)
        return 0;
    if(dp[idx][flag]!=-1)
        return dp[idx][flag];
    ll val=(flag==1)?b[idx]:1;
    ll left=solve(idx+1,0)+val-1;
    ll right=solve(idx+1,1)+abs(val-b[idx+1]);
    return (dp[idx][flag]=max(left,right));
}

int main()
{ 
ll t;
cin>>t;
while(t--)
{ 
cin>>n;
memset(dp,-1,sizeof(dp));
ll res=0;
for(int i=0;i<n;i++)
    cin>>b[i];
ll s1=solve(0,0);
ll s2=solve(0,1);
cout<<max(s1,s2)<<"\n";
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-26
    • 2011-10-17
    • 1970-01-01
    相关资源
    最近更新 更多