一个经典的板子dp题目,时间复杂度不高,可以使用经典dp形式

我们定义dp[r][p],就是第r个花朵放在第p个花瓶的美观度(有点类似背包),首先进行剪枝操作:由于每个花都要放,第i朵花只能放在第i个花瓶到第v - (f - i)个花瓶之间,这是为了预留足够的空间

之后对于每一种dp[r][p],我们有状态转移方程

dp[r][y] = max(dp[r][y],dp[r - 1][i] + beauty[r][y]);

这是这道题最关键的地方,主要是我们如果使用传统的背包方式来进行取max的话,就会出现问题:对于dp[r - 1][y - m],这个m到底应该是减到多少?由于我们这里的dp是维护当前放置时的最大值的。所以说我们就无法用传统的背包问题n2来解决

这里我们使用:先把所有未处理的dp初始化为LLONG_MIN,之后对于每个dp[r][y],都枚举之前的所有位置,看看对应的dp[r - 1][i] + 当前的美观值是什么之后取最大值max。也不用担心由于负数而可能导致的(取max结果没有计算到当前这个元素因为美观值是负的)情况,因为初始化为LLONG_MIN,再怎么乱搞也不会更小,最终结果一定是已经包含的当前美观度考虑的情况

对于回溯,我们直接从我们已有的dp数组入手,从最后一个花开始,每一层开始回溯,找到了对应的值就记录之后继续上一层即可。对于尽可能靠前的情况,只需要内层循环从最开始计数就可以了 (由于是每一层的统计,模拟了我们当初选取的过程,也就是一种逆向的重新来拿,自然也就不用担心顺序错乱的问题了,如果有,那就是你顺序的dp就写错了)

AC代码:

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

signed main() 
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    //修补骑士被背包问题害惨了,实际上100的数据量可以用一些很神秘的复杂度的(n3差不多200左右)
    
    int f,v;
    
    cin>>f>>v;
    
    vector<vector<int>> beauty(f,vector<int>(v,0));
    
    for(int r = 0;r < f;r++)
    {
        for(int p = 0;p < v;p++)
        {
            cin>>beauty[r][p];
        }
    }

    vector<vector<int>> dp(f,vector<int>(v,LLONG_MIN));
    
    for(int r = 0;r <= v - f;r++)
    {
        dp[0][r] = beauty[0][r];
    }
    
    for(int r = 1;r < f;r++)
    {
        for(int y = r;y <= v - (f - r);y++)
        {   
            for(int i = r - 1;i <= y - 1;i++)
            {
                if(dp[r - 1][i] != LLONG_MIN)
                {
                    dp[r][y] = max(dp[r][y],dp[r - 1][i] + beauty[r][y]);
                }
                else
                {
                    continue;
                }
            }
        }
    }
    
    int ans = *max_element(dp[f - 1].begin(),dp[f - 1].end());
    
    vector<int> remit;
    
    int aans = ans;
    
    for(int r = f - 1;r >= 0;r--)
    {
        for(int u = 0;u <= v - 1;u++)
        {
            if(dp[r][u] == aans)
            {
                aans -= beauty[r][u];
                
                remit.push_back(u + 1);
                
                break;
            }
        }
    }
    
    reverse(remit.begin(),remit.end());
    
    cout<<ans<<endl;
    
    for(int p : remit)
    {
        cout<<p<<" ";
    }
    
    return 0;
}