题目链接:313. 花店橱窗 - AcWing题库
输入样例:
3 5
7 23 -5 -24 16
5 21 -4 10 23
-21 5 -4 -20 20
输出样例:
53
2 4 5
题意很明确,每一行需要取一个数,且这些数出现在不同列且行数小的数所在的列数也小。
思路分析:
令f[i][j]表示在1~j列中选取了i个数的合法方案的最大权值和,很显然j>=i,需要注意的一点是对于f[i][j]不一定选了第i行第j列的数,至于更新过程也比较容易,我们考虑f[i][j]的来源,第一种情况是f[i][j-1],也就是说在1~j列中选取了i个数的合法方案的最大权值和中第i行选取的数并不是第j个,而是第1~j-1个数中的一个,另一种情况是f[i-1][j-1]+a[i][j],也就是说在1~j列中选取了i个数的合法方案的最大权值和中第i行选取的数就是第j个数,这个也比较容易理解,同理需要注意的一点就是f[i-1][j-1]也并不代表我们选取了第i-1行的第j-1列的数,只要明白了这些这道题基本上就没什么了。
输出路径的时候我们只需要令path[i][j]表示第i行选取的数的位置即可,前提是j大于第i行最优答案数所在的位置,再分别根据f[i][j]的来源来更新路径,如果f[i-1][j-1]+a[i-1][j-1]>f[i][j-1],这意味着我们第i行选的数就是j,那么令path[i][j]=j即可,否则若f[i-1][j-1]+a[i-1][j-1]<=f[i][j-1](注意等号的位置不能换地方,这是由于字典序所决定的),这意味着我们第i行选的数的位置记录在了path[i][j-1]中,所以我们直接令path[i][j]=path[i][j-1]即可。
然后我们直接一个while循环存下最优答案中每行选取的数的位置即可。
下面是代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=103;
int a[N][N],f[N][N],path[N][N];
int st[103],top=0;
int main()
{
int n,m;
cin>>n>>m;
memset(f,-0x3f,sizeof f);
for(int i=0;i<=m;i++)
f[0][i]=0;
for(int i=0;i<=n;i++)
f[i][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=i;j<=m;j++)
{
if(j!=i)
{
if(f[i][j-1]>=f[i-1][j-1]+a[i][j])
{
f[i][j]=f[i][j-1];
path[i][j]=path[i][j-1];
}
else
{
f[i][j]=f[i-1][j-1]+a[i][j];
path[i][j]=j;
}
}
else
{
f[i][j]=f[i-1][j-1]+a[i][j];
path[i][j]=j;
}
}
int ans=-0x3f3f3f3f;
int t=0;
for(int i=n;i<=m;i++)
{
if(ans<f[n][i])
{
ans=f[n][i];
t=i;
}
}
while(n)
{
st[++top]=t;
n--;
t=path[n][t-1];
}
printf("%d\n",ans);
for(int i=top;i>=1;i--)
printf("%d ",st[i]);
return 0;
}