花店橱窗布置题目见洛谷:https://www.luogu.org/problem/show?pid=1854
一.记忆化搜索
这个问题第一眼看上去,它就是一个当初深搜求组合数的方案数的变形,m个花瓶插n朵花,而且这几朵花的顺序先后顺序不变,求和最大.是否有点像m个数取n个数组合,这n个数保证小的在前,大的在后,只不过是输出方案数而非和呢.看看数据规模100以内,想想,裸搜是一定会TLE的,但还是大胆地搜试试,果然AC1个点,9个点超时。
想想这道题既然可以用动规解决,那在搜的过程中用适当的数组记下搜索产生的过程最优值,即记忆化搜索的方式对搜索剪枝也一定可以完成。于是采用了一个数组rem[101][101]来记录搜索过程中的最优值。如果之前对该第k朵花放至第t个花瓶及下方搜索完毕过,则直接返回rem[k][t],否则以搜索所有可能的情况,记录下最优值至rem[k][t]里。
以样例数据为例,记忆化搜索树图如下,辅助理解:
#include<iostream>
#include<climits>
#include<iomanip>
using namespace std;
int n,m;
int flower[101][101];
int rem[101][101],can[101][101];
int dfs(int k,int t){
if(rem[k][t]!=0) return rem[k][t] ;
if(k==n){
return rem[k][t] = flower[k][t];
}
int ansM=INT_MIN,temp=0;
for(int j = t+1;j<= m-(n-k)+1; j++){
temp = dfs(k+1,j) + flower[k][t];
if(ansM < temp){
ansM = temp;
can[k][t] = j;
}
}
rem[k][t] = ansM;
return ansM;
}
int main(){
cin >> n >> m;
for(int i = 1; i<= n; i++)
{
for(int j = 1; j<= m; j++)
cin >> flower[i][j];
}
cout << dfs(0,0) << endl;
int x = INT_MIN,s=0;
s = can[0][0];
cout << s << " ";
for(int i = 1; i< n; i++)
{
cout << can[i][s] <<" ";
s = can[i][s];
}
cout << endl;
return 0;
}
二.动态规划
如图,可以看到是一个有向无环图,题目要求的是这个图的最长路径,一个比较好的算法是动态规划.对于第i束花放至第j个花瓶时的可能产生的最美值,实际是前i-1束花放至第i-1 ~ j-1之间的最优值 + flower[i][j].因此可以以放入的花束个数为阶段,动态规划状态转移方程为:dp[i][j] = max(dp[i][j],dp[i-1][k] + flower[i][j]),并同时记录这个最大值由哪个k来更新的,方便后期输出方案.
详细程序如下:
#include<iostream>
#include<climits>
using namespace std;
int n,m;
int flower[101][101],dp[101][101];
int ans = INT_MIN,d=0;
int v[101],ansv[101];
int pat[101][101];
int main(){
cin >> n >> m;
for(int i = 1; i<= n; i++){
for(int j = 1; j<= m; j++)
cin >> flower[i][j];
}
for(int i = 1; i<= n; i++){
dp[1][i] = flower[1][i];
}
for(int i =2; i<= n; i++) //i代表花束
{
for(int j = i; j <= m-(n-i);j++){
for(int k = i-1; k< j; k++){
if(dp[i][j] < dp[i-1][k] + flower[i][j]) {
dp[i][j] = dp[i-1][k] + flower[i][j];
pat[i][j] = k;
}
}
}
}
int s;
for(int i = n; i<=m; i++){
if(ans<dp[n][i]){
ans = dp[n][i];
s = i;
}
}
cout << ans << endl;
for(int i=n; i>= 1; i--){
v[i] = s;
s = pat[i][s];
}
for(int i=1 ;i <= n; i++){
cout << v[i] << " ";
}
return 0;
}
…