洛谷P1854 花店橱窗布置
本文侧重于讲解在考试中如何骗分拿到更高的分数:)
10分方法:深度优先搜索DFS,用于考试时间将尽急需自救时~~(毕竟能多一分是一分)~~
我们可以将输入看成一个数据矩阵,第 x x x行对应第 x x x朵花,第 y y y列对应第 y y y个花瓶、
首先遍历前( v a s e vase vase_ n u m num num - f l o w e r flower flower_ n u m num num)个花瓶,即第一朵花位置的所有可能情况,对于每一种情况分别进行搜索,搜索参数为 ( 第一朵花,第 i 个花瓶,第 1 朵花在第 i 个花瓶中的美学值 ) (第一朵花,第i个花瓶,第1朵花在第i个花瓶中的美学值) (第一朵花,第i个花瓶,第1朵花在第i个花瓶中的美学值)
for(int i = 1; i + flower_num <= vase_num; i++){//遍历花瓶
search(1, i, beauty_num[1][i]);
}
搜索过程为:对于下一朵花,行数 + 1 +1 +1,列数 i i i需满足大于当前列数 j j j且小于总列数 v a s e vase vase_ n u m num num,即必须插在后面的花瓶中(满足题目要求的按“按标号顺序排列”),美学值总和加上这朵花在第 i i i列的美好值
for(int i = y+1; i <= vase_num; i++){
search(x+1, i, sum + beauty_num[x+1][i]);//下一朵花,插入当前花瓶时的美学值
}
对于每一轮搜索,我们需要记录当前花与花瓶的情况,即使用标记数组 r o a d road road记录“路径”。在搜到最后一个花瓶时搜索结束,更新最大美学值并更新最佳摆放方式
road[x] = y;
if(x == flower_num){
if(sum > _ans){
_ans = sum;
for(int i = 1; i <= flower_num; i++){
ans[i] = road[i];
}
}
return ;
}
完整10分代码:
#include <iostream>
#include <cstdio>
using namespace std;
int flower_num , vase_num , num;
int cnt , maxx , _ans;
int beauty_num[105][105];
int road[105];//第i行选择的是哪个花瓶
int ans[105];
void search(int x , int y , int sum){
road[x] = y;
if(x == flower_num){
if(sum > _ans){
_ans = sum;
for(int i = 1; i <= flower_num; i++){
ans[i] = road[i];
}
}
return ;
}
for(int i = y+1; i <= vase_num; i++){
search(x+1, i, sum + beauty_num[x+1][i]);//下一朵花,插入当前花瓶时的美学值
}
}
int main(){
cin >> flower_num >> vase_num;
for(int i = 1; i <= flower_num; i++)
for(int j = 1; j <= vase_num; j++){
cin >> beauty_num[i][j];
}
for(int i = 1; i + flower_num <= vase_num; i++){//遍历花瓶
search(1, i, beauty_num[1][i]);
}
cout << _ans << endl;
for(int i = 1; i <= flower_num; i++){
cout << ans[i] << " ";
}
return 0;
}
满分歪解:记忆化DFS
洛谷数据较水所以能AC哈哈哈哈
浅估一下,以CCF的毒瘤数据…大约能拿60分
记忆化的优化是一种以空间换时间的方法,常用于骗分自救
记忆化搜索也可看这篇博客
记忆化的关键在于记录先前搜索到的情况,并在之后遇到相同且总情况比先前差的点时及时停止继续搜索,及时止损,减少不必要的时间消耗。在我的代码中,使用了 m e m o r y memory memory数组记录先前数据
具体操作时,在DFS函数的最开始进行判断,如果当前点不是第一次被搜索,且已经搜到过更好情况,就及时结束本次搜索。如若不然就将 m e m o r y memory memory数组中对应位置的情况更新为当前情况。↓
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int flower_num , vase_num , num;
int cnt , maxx , _ans;
int beauty_num[105][105];
int memory[105][105];//记忆化数组
int road[105];//第i行选择的是哪个花瓶
int ans[105];
void search(int x , int y , int sum){
if(sum < memory[x][y])return ;
else memory[x][y] = sum;
road[x] = y;
if(x == flower_num){
if(sum > _ans){
_ans = sum;
for(int i = 1; i <= flower_num; i++){
ans[i] = road[i];
}
}
return ;
}
for(int i = y+1; i <= vase_num; i++){
search(x+1, i, sum + beauty_num[x+1][i]);//下一朵花,插入当前花瓶时的美好值
}
}
int main(){
// freopen("code.in", "r", stdin);
// freopen("code.out", "w", stdout);
cin >> flower_num >> vase_num;
memset(memory , -0x7f7f7f7f, sizeof(memory));
for(int i = 1; i <= flower_num; i++)
for(int j = 1; j <= vase_num; j++){
cin >> beauty_num[i][j];
}
for(int i = 1; i + flower_num <= vase_num; i++){//遍历花瓶
search(1, i, beauty_num[1][i]);
}
cout << _ans << endl;
for(int i = 1; i <= flower_num; i++){
cout << ans[i] << " ";
}
return 0;
}
满分正解DP
时间复杂度 O ( n 2 ) O(n^2) O(n2)
核心数组为 d p [ 105 ] [ 105 ] dp[105][105] dp[105][105],而 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个花瓶装 j j j朵花时的最大美学值
对于 d p [ i ] [ j ] dp[i][j] dp[i][j],可以有两种情况:
- j j j朵花存在前 i − 1 i-1 i−1个花瓶中,即 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]
- j j j朵花存在当前所有的 i i i个花瓶中,那么第 j j j朵花一定存在第 i i i个花瓶中,且前 j − 1 j-1 j−1朵花存在前 i − 1 i-1 i−1个花瓶中。那么此情况下的美学值为 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1] + b e a u t y beauty beauty_ n u m num num [ j ] [ i ] [j][i] [j][i]
那么就可以得出本题的状态转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
1
]
+
b
e
a
u
t
y
dp[i][j]=max(dp[i-1][j], dp[i-1][j-1]+beauty
dp[i][j]=max(dp[i−1][j],dp[i−1][j−1]+beauty_
n
u
m
[
j
]
[
i
]
)
num[j][i])
num[j][i])
对 d p dp dp数组的初始化也必不可少。因为美学值之和可能为负,所以要将 d p dp dp数组初始化为一个极小值。但是因为转移方程的第二部分, d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1]在此情况下可能为极小值( d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0]),所以要将只有第 1 1 1朵花的情况先进行判断,再从 2 2 2朵花的情况开始DP
只有一朵花时, d p [ i ] [ 1 ] = m a x x ( d p [ i − 1 ] [ 1 ] , b e a u t y dp[i][1] = maxx(dp[i-1][1] , beauty dp[i][1]=maxx(dp[i−1][1],beauty_ n u m [ 1 ] [ i ] ) num[1][i]) num[1][i]),即用所有花瓶装第1朵花时的最大美学值
输出时,从最后一行开始倒序推导,找到每一行(每朵花)对应的花瓶,再将总美学值更新为总美学值与这朵花对应的美学值之差,再进行下一轮判断,找到每朵花对应的花瓶,再倒序输出
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int flower_num , vase_num;
int beauty_num[105][105];
int dp[105][105];//dp[i][j]表示前i个花瓶装j朵花时的最大美学值
int ans[105] , cnt;
int maxx(int x, int y){
return x > y ? x : y;
}
void display(){//输出dp数组看一下,以此推导有花花瓶的位置以及输出方法
cout << endl;
for(int i = 1; i <= flower_num; i++){
for(int j = 1; j <= vase_num; j++){
cout << dp[j][i] << " ";
}
cout << endl;
}
}
void print(){
int num = dp[vase_num][flower_num];
for(int i = flower_num; i >= 1; i--){
for(int j = 1; j <= vase_num; j++){
if(dp[j][i] == num){
ans[++cnt] = j;//存花瓶位置
num -= beauty_num[i][j];
break;//去进行下一朵花的判断
}
}
}
for(int i = cnt; i >= 1; i--)
cout << ans[i] << " ";
}
int main(){
memset(dp, -0x7f7f7f7f, sizeof(dp));//避免最大美学值仍然小于0,因此将最大美学值改为0的情况出现
// freopen("code.in", "r", stdin);
// freopen("code.out", "w", stdout);
cin >> flower_num >> vase_num;
for(int i = 1; i <= flower_num; i++)
for(int j = 1; j <= vase_num; j++){
cin >> beauty_num[i][j];
}
for(int i = 1; i <= vase_num; i++)
dp[i][1] = maxx(dp[i-1][1] , beauty_num[1][i]);
for(int j = 2; j <= flower_num; j++)
for(int i = 1; i <= vase_num; i++){
dp[i][j] = maxx(dp[i-1][j], dp[i-1][j-1] + beauty_num[j][i]);
}
//display();
cout << dp[vase_num][flower_num] << endl;
print();
return 0;
}