骗分大法咯咯咯

洛谷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],可以有两种情况:

  1. j j j朵花存在前 i − 1 i-1 i1个花瓶中,即 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]
  2. j j j朵花存在当前所有的 i i i个花瓶中,那么第 j j j朵花一定存在第 i i i个花瓶中,且前 j − 1 j-1 j1朵花存在前 i − 1 i-1 i1个花瓶中。那么此情况下的美学值为 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1] + 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[i1][j],dp[i1][j1]+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[i1][j1]在此情况下可能为极小值( d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i1][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[i1][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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值