洛谷 P1731 [NOI1999] 生日蛋糕

题目

题目链接

自己没看题解写的,摸石头过河,解释一下

首先,输入输出都是正整数。先搞定输入,再判断条件,如果无解,输出0,否则输出蛋糕外表面面积Q(这里用全局变量,开long long)。

然后写dfs函数,函数形参先写了一个layer, r, h。这些数据是需要递归时传入的,在每一次的搜索中,r,h都会变。

先写结束条件,搜索层数layer与输入层数m相同时,return结束。后来写着写着又在形参加了两个变量,一个是奶油面积total_CreamArea(蛋糕外表面面积),一个是搜索中的总体积。结束条件中另外再加一个判断就是当总体积totalvolume = n时,取Q和奶油面积total_CreamArea的最小值来更新Q的值

写到这里就有问题了,遇到瓶颈。这个r,h的范围是多少呢?

#include <bits/stdc++.h>
using namespace std;

int n, m; //蛋糕体积,层数
long long Q = 9e10;

void dfs(int layer, int r, int h,int total_CreamArea, int totalvolume)
{
    if(layer == m) //层数 = 输入层数
    {
        if(totalvolume == n)
            Q = min(Q, total_CreamArea);
        return; //结束dfs
    }
    //for(int i = r; i>=0; i--)
    int newArea = total_CreamArea + 2*r*h; //加上侧面积,底面积就是第一层的大圆面积
    int newVolume = totalvolume + r*r*h; //加上新一层的体积
        dfs(layer+1, r, h, newArea, newVolume);
}

int main()
{
    
    cin>>n>>m;
    dfs(1, )
    if(Q == 9e10)
        cout << 0 << endl;
    else
        cout << minvolume << endl;
}

后来看了视频,还有罗老师的博客,才知道原来是这样确定r, h的范围的。当然剪枝也是不能少的,没有剪枝就会超时。

我是推不出来这个条件:2(n−v)/r+s≥ans,测试过没有这个条件也有70分!


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/weixin_43914593/article/details/135489983

【AC参考代码】

#include <bits/stdc++.h>
using namespace std;

int n, m; //蛋糕体积,层数
int Q = 9e8;
int minVolume[16], minArea[16]; //用于剪枝

void dfs(int layer, int r, int h,int total_CreamArea, int totalvolume)
{
    if(layer == 0) //层数 = 输入层数
    {
        if(totalvolume == n)
            Q = min(Q, total_CreamArea);
        return; //结束dfs
    }
    if(minVolume[layer] + totalvolume > n || totalvolume > n) //体积大于输入值
        return;
    if(total_CreamArea + minArea[layer] >= Q) //面积比最小面积还要大,舍去
        return;
    if(2*(n - totalvolume)/r + total_CreamArea >= Q) //2(n−v)/r+s≥ans
    	return;
    for(int newR = min(r-1, (int)(sqrt(n-totalvolume)) ); newR >= layer; newR--)
    {
    	if(layer == m) //上表面积,从上往下看就是一个圆
    		total_CreamArea = newR * newR;
                                 //体积公式求h
        for(int newH = min(h-1, (n-totalvolume)/(newR * newR) ); newH >= layer; newH--)
        {
            int newArea = total_CreamArea + 2*newR*newH; //加上侧面积,底面积就是第一层的大圆面积
            int newVolume = totalvolume + newR*newR*newH; //加上新一层的体积
            dfs(layer-1, newR, newH, newArea, newVolume);
        }
    }
    
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        minVolume[i] = minVolume[i-1] + i*i*i; //R^3
        minArea[i] = minArea[i-1] + 2*i*i; //2*R^2
    }
    dfs(m, 9e8, 9e8, 0, 0);
    if(Q == 9e8)
        cout << 0 << endl;
    else
        cout << Q << endl;
}

看视频吧,视频肯定比我讲得清楚。视频链接:【B24 DFS剪枝 生日蛋糕】

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
P2375 [NOI2014] 动物园是一道经典的动态规划题目,以下是该题的详细题意和解题思路。 【题意描述】 有两个长度为 $n$ 的整数序列 $a$ 和 $b$,你需要从这两个序列中各选出一些数,使得这些数构成一个新的序列 $c$。其中,$c$ 序列中的元素必须在原序列中严格递增。每个元素都有一个价值,你的任务是选出的元素的总价值最大。 【解题思路】 这是一道经典的动态规划题目,可以采用记忆化搜索的方法解决,也可以采用递推的方法解决。 记忆化搜索的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int dfs(int x, int y) { if (dp[x][y] != -1) return dp[x][y]; if (x == n || y == n) return 0; int res = max(dfs(x + 1, y), dfs(x + 1, y + 1)); if (a[x] > b[y]) { res = max(res, dfs(x, y + 1) + b[y]); } return dp[x][y] = res; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); memset(dp, -1, sizeof(dp)); printf("%d\n", dfs(0, 0)); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值,-1表示未计算过。dfs(x,y)表示选到a数组中第x个元素和b数组中第y个元素时的最大价值,如果dp[x][y]已经计算过,则直接返回dp[x][y]的值。如果x==n或者y==n,表示已经遍历完一个数组,直接返回0。然后就是状态转移方程了,如果a[x] > b[y],则可以尝试选b[y],递归调用dfs(x, y+1)计算以后的最大价值。否则,只能继续遍历数组a,递归调用dfs(x+1, y)计算最大价值。最后,返回dp[0][0]的值即可。 递推的代码如下: ```c++ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1005; int dp[MAXN][MAXN], a[MAXN], b[MAXN], n; int main() { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); for (int i = 0; i < n; i++) scanf("%d", &b[i]); for (int i = n - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]); if (a[i] > b[j]) { dp[i][j] = max(dp[i][j], dp[i][j + 1] + b[j]); } } } printf("%d\n", dp[0][0]); return 0; } ``` 其中,dp[i][j]表示选到a数组中第i个元素和b数组中第j个元素时的最大价值。从后往前遍历数组a和数组b,依次计算dp[i][j]的值。状态转移方程和记忆化搜索的方法是一样的。 【参考链接】 P2375 [NOI2014] 动物园:https://www.luogu.com.cn/problem/P2375
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值