一个经典的板子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;
}