这里提供一个 $O(nm)$ 复杂度做法。
记 $f(i,j)$ 表示前 $i$ 个花瓶放了 $j$ 朵花的最大美观程度,那么每个状态只有两个选择,要么选这个花瓶要么不选,并且如果选了这个花瓶便只能放第 $j$ 朵花,这样的设计自然地保证了花的位置与顺序不会冲突。
状态转移方程(有点像背包):$$f(i,j)=\max\left(f(i-1,j),f(i-1,j-1)+w(i,j)\right) $$ 其中 $w(i,j)$ 表示第 $j$ 朵花在第 $i$ 个花瓶的美观度。
起点:$f(i,0)=0\ (0\leq i\leq n)$,其余为负无穷。
目标:$\max\left(f(i,m)\right)\ (m\leq i\leq n)$,$m$ 为花种数,$n$为花瓶数。
(好像还可以压成一维……
#include <bits/stdc++.h>
using namespace std;
int n,m,ans=-1e9,ed,f[105][105],w[105][105];
struct Path{int x,y;}p[105][105];
//记路径的数组,p[i][j].x表示(i,j)这个状态是从哪个状态转移过来的
//p[i][j].y表示(i,j)这个状态是选了哪个花瓶后被更新的
void print(int now,int cnt)
{
if(!cnt) return;
print(p[now][cnt].x,cnt-1);
printf("%d ",p[now][cnt].y);
}
int main()
{
scanf("%d%d",&m,&n); //m,n意义如上
for(int i=1;i<=m;++i)
for(int j=1;j<=n;++j)
scanf("%d",&w[j][i]); //反过来读
memset(f,0xcf,sizeof(f));
for(int i=0;i<=n;++i) f[i][0]=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=min(m,i);++j)
{
f[i][j]=f[i-1][j]; p[i][j]={p[i-1][j].x,p[i-1][j].y};
if(f[i][j]<f[i-1][j-1]+w[i][j])
f[i][j]=f[i-1][j-1]+w[i][j],p[i][j]={i-1,i};
}
for(int i=m;i<=n;++i) ans=max(ans,f[i][m]);
for(int i=m;i<=n;++i) if(f[i][m]==ans) {ed=i; break;}
printf("%d\n",ans);
print(ed,m);
return 0;
}