题面: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 ] 表示从第一行走到第 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]; } }
②用 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; }
原文地址:https://www.cnblogs.com/iss-ue/p/12491921.html
时间: 2024-10-13 02:14:00