花店橱窗布置

题面:https://www.luogu.com.cn/problem/P1854

给定一个 n * v 的矩阵

要求从第一行走到第f行,每行取走一个数,

且该行所取的数必须必上一行所取的数的列数大 , 求所能取走的最大值

注意每一行所取走的数字的列数必须大于等该行的行号

因为必须给前面的花留下足够的花瓶


由此我们便可以很容易的得出状态转移方程

 dp [ i ] [ j ] = max ( dp [ i-1 ] [ k ] ) + d [ i ] [ j ] ( k < j ) 

 dp [ i ] [ j ] = max ( dp [ i-1 ] [ k ] ) + d [ i ] [ j ] ( k < j ) 

 dp [ i ] [ j ] = max ( dp [ i-1 ] [ k ] ) + d [ i ] [ j ] ( k < j ) 

其中dp [ i ] [ j ] 表示从第一行走到第 i 行并取走该行第j个数所能取得的最大值

用字符串数组保留方案

设置string数组 an [ i ] [ j ] , 在dp数组转移状态时也一起转移

我们知道string是可以直接相加的,那么转移的时候如果继承上一个状态更优

那字符数组就由上一个状态加上这次的选择,也就是

if(dp[i-1][q]+a[i][j]>dp[i][j])
{
    dp[i][j]=max(dp[i][j],dp[i-1][q]+a[i][j]);
    an[i][j]=an[i-1][q]+zhuan(j);//这次是放在了j位置
    //zhuan函数是把数字变成字符串的函数 
}

完整代码

#include <iostream>
using namespace std;
int n,m;
int a[109][109];
int dp[109][109];
string an[109][109];
string zhuan(int s){
    string k,q;
    while(s){
        k+=(s%10+'0');
        s/=10;
    }
    for(int i=k.length()-1;i>=0;i--)
        q+=k[i];
    q+='-';
    return q;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            cin>>a[i][j];
    }
    //第i种花放在j位置 
    for(int i=1;i<=m;i++)    dp[1][i]=a[1][i],an[1][i]=zhuan(i);
    int maxn=0;
    for(int i=2;i<=n;i++)
    {
        for(int j=i;j<=m;j++)
        {
            for(int q=1;q<j;q++)
            {
                if(dp[i-1][q]+a[i][j]>dp[i][j])
                {
                    dp[i][j]=max(dp[i][j],dp[i-1][q]+a[i][j]);
                    an[i][j]=an[i-1][q]+zhuan(j);
                    //zhuan函数是把数字变成字符串的函数 
                }
            }
        }
    }
    int num;
    for(int i=1;i<=m;i++)
    {
        if(maxn<dp[n][i])
        {
            num=i;
            maxn=max(maxn,dp[n][i]);
        }
    }
    cout<<maxn<<endl;
    for(int i=0;i<an[n][num].length();i++)
    {
        if(an[n][num][i]=='-')    cout<<" ";
        else    cout<<an[n][num][i];
    }
} 
View Code

用 int 数组保存方案

同样的,定义pre [ i ] [ j ] 为让 dp [ i ] [ j ] 最大时上一个状态选的什么

初始化没有上一个状态,所以指向自己

    for(int i=1;i<=m;i++)    dp[1][i]=a[1][i],pre[1][i]=i;

然后我们和dp数组一起转移就是了

输出方案的时候一路倒推回去

int ans[109],cnt=n;
    ans[n]=num;//最后一个pre记录不到,手动输入 
    while(pre[cnt][num]!=num)
    {
        ans[cnt-1]=pre[cnt][num];
        num=pre[cnt][num],cnt--;
    }
    for(int i=1;i<=n;i++)
        cout<<ans[i]<<" ";

二、跑dijtls最长路(懂什么意思就行,错是肯定写错了)

暂时不是很懂,先贴下别人代码。

//楼下全是dp,那么来个最长路做法 
//可以将这个看成一个以花为横,瓶为纵的表格从下向上找一条路径,上一排的位置必须小于下一排
//那么我们就可以连边去跑最长路了
//同时我们注意到需要记录路径
//那么这里就选dij好了(因为其他的不会记路径啊) 
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#pragma GCC optimize(3)
#define re register
#define maxn 10001
#define maxw 800000
#define inf -99999999
using namespace std;
struct node
{
    int v,w,nxt;
}e[maxw];
int f,v,ans,num=1,end;
int head[maxn],d[maxn],r[maxn];
int a[101][101];
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,less<pii> > q;
inline int read()
{
    char c=getchar();
    int x=0;
    int r=1;
    while(c<'0'||c>'9') 
    {
        if(c=='-') r=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=x*10+c-48;
        c=getchar();
    }
    return r*x;
}
inline void add_edge(int x,int y,int z)
{
    e[num].v=y;
    e[num].w=z;
    e[num].nxt=head[x];
    head[x]=num++;
}
inline void dijkstra(int s)
{
    for(re int i=1;i<=f*v;i++)
      d[i]=inf;
    d[s]=0;
    q.push(make_pair(d[s],s));
    while(q.size())
    {
        pii mid=q.top();
        q.pop();
        int k=mid.second;
        for(re int i=head[k];i;i=e[i].nxt)
        if(d[e[i].v]<d[k]+e[i].w)
        {
            d[e[i].v]=d[k]+e[i].w;
            q.push(make_pair(d[e[i].v],e[i].v));//很常规的松弛操作 
            r[e[i].v]=k;//存一下前驱结点,用来找路径 
        }
    }
}
void dfs(int i)
{
    if(i!=0) dfs(r[i]);
    if(i!=0) cout<<i%v<<" ";//dfs找出路径,再将点还原成二维 
}
int main()
{
    f=read();
    v=read();
    for(re int i=1;i<=f;i++)
    for(re int j=1;j<=v;j++)
    a[i][j]=read();
    for(re int i=1;i<=v;i++)
      add_edge(0,i,a[1][i]);//我们把0作为起点,把第一列的所有点与0相连 
    for(re int i=2;i<=f;i++)
    for(re int k=1;k<=v;k++)
    for(re int j=k+1;j<=v;j++)
      add_edge((i-2)*v+k,(i-1)*v+j,a[i][j]);//把点的坐标压成一维 
      //连边,需要注意的是上一行点的横坐标要小于下一行的 
    dijkstra(0);
    ans=inf;
    for(re int i=1;i<=v;i++)
    if(ans<d[(f-1)*v+i])//从最后一行找最长路 
    {
        ans=d[(f-1)*v+i];
        end=(f-1)*v+i;
    }
    cout<<ans<<endl;
    dfs(r[end]);
    end%=v;
    if(end==0) end=v;
    cout<<end<<endl;
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/iss-ue/p/12491921.html