Greedy Tino - 九度教程第 100 题

解决九度教程第100题,题目要求在限制条件下找到能平衡放置的最大柑橘重量。通过动态规划策略,计算每一步状态转移,找出最大重量。输入包含测试案例数和每个案例的柑橘重量,输出为每堆最大重量或-1表示无法平衡。
摘要由CSDN通过智能技术生成

Greedy Tino - 九度教程第 100 题

题目

时间限制:1 秒 内存限制:32 兆 特殊判题:否
题目描述:
Tino wrote a long long story. BUT! in Chinese.So I have to tell you the problem directly and discard his long long story. That is tino want to carry some oranges with “Carrying pole”, and he must make two side of the Carrying pole are the same weight. Each orange have its’ weight. So greedy tino want to know the maximum weight he can carry.
输入:
The first line of input contains a number t, which means there are t cases of the test data.for each test case, the first line contain a number n, indicate the number of oranges.the second line contains n numbers, Wi, indicate the weight of each orange.n
is between 1 and 100, inclusive. Wi is between 0 and 2000, inclusive. the sum of Wi is
equal or less than 2000.
输出:
For each test case, output the maximum weight in one side of Carrying pole. If you can’t carry any orange, output -1. Output format is shown in Sample Output.
样例输入:
1
5
1 2 3 4 5
样例输出:
Case 1: 7

本题大意:有一堆柑橘,重量为 0 到 2000,总重量不大于 2000。要求从中取出两堆放在扁担的两头且两头的重量相等,问符合条件的每堆重量最大为多少。没有符合条件的分堆方式则输出-1。

在求解该问题之前,先关注本题的输入特点。与以往讨论过的问题不同,该例在输入中将预先告诉我们输入的测试数据个数,即整数 T。所以程序只需要准确的处理 T 组数据即可,以免造成不必要的错误。

首先,只考虑柑橘重量为非 0 的情况。因为本题要求解的是重量相等的两堆柑橘中每堆的最大重量,并且在堆放过程中,由于新的柑橘被加到第一堆或者第二堆,两堆之间的重量差会动态发生改变,所以设状态 dp[i][j]表示前 i 个柑橘被选择后(每个柑橘可能放到第一堆或者第二堆)后,第一堆比第二堆重 j 时(当 j 为负时表示第二堆比第一堆重),两堆的最大总重量和

初始时,dp[0][0]为 0,即不往两堆中加任何柑橘时,两堆最大总重量为 0;dp[0][j](j 不等于 0)为负无穷,即其它状态都不存在。

根据每一个新加入的柑橘被加入到第一堆或者第二堆或者不加到任何一堆,设当前加入柑橘重量为 list[i],这将造成第一堆与第二堆的重量差增大 list[i]或减小 list[i]或者不变,我们在它们之中取最大值,其状态转移为:

dp[i][j]=max(dp[i-1][j-list[i]]+list[i], dp[i-1][j+list[i]+list[i], dp[i-1][j])

当根据该状态转移方程求出所有的状态后,状态 dp[n][0] / 2 即是所求。

再来考虑柑橘重量包含0的情况,当在不考虑柑橘重量为0,推得dp[n][0]为正数时,柑橘重量为 0 的柑橘将不对答案造成任何影响,固在这种情况时可直接排除重量为 0 的柑橘。当在不考虑柑橘重量为 0,推得 dp[n][0]为 0 时,即不存在任何非 0 的组合使两堆重量相等。此时,若存在重量为 0 的柑橘,则可组成两堆重量为 0 的柑橘(至少有一个柑橘重量为 0),它们重量相等;否则,将不存在任何分堆方式,输出-1。

最后分析其复杂度。由于柑橘总重量不大于 2000,所以总的状态数量为柑橘总数 n*2*2000,状态转移为 O(1)复杂度,所以综合时间复杂度为O(4000*m),在我们可以接收的范围内。

#include <stdio.h>

#define OFFSET 2000
//因为柑橘重量差存在负数的情况,即第一堆比第二堆轻
//所以在计算重量差对应的数组下标时加上该偏移值,
//使每个重量差对应合法的数组下标

int dp[101][4001];//保存状态 (-2000~2000)
int list[101];//保存柑橘数量
#define INF 0x7fffffff//无穷

int main()
{
    int T;
    int cas=0;//处理的case数,以便输出
    scanf("%d",&T);//输入要处理的数据组数
    while(T--){//T次循环
        int n;
        scanf("%d",&n);
        bool HaveZero=false;//统计是否存在重量为0的柑橘
        int cnt=0;//计数器,记录共有多少个重量非零的柑橘

        for(int i=1;i<=n;i++){
            //输入n个柑橘重量
            scanf("%d",&list[++cnt]);
            if(list[cnt]==0){//若当前输入柑橘重量为0
                cnt--;//去除这个柑橘
                HaveZero=true;//并记录存在重量为0的柑橘
            }
        }
        n=cnt;

        for(int i=-2000;i<=2000;i++){
            dp[0][i+OFFSET]=-INF;
        }//初始化,所有dp[0][i]为负无穷
        //注意要对重量差加上OFFSET后读取或调用

        dp[0][0+OFFSET]=0;//dp[0][0]为0
        for(int i=1;i<=n;i++){//遍历每个柑橘
            for(int j=-2000;j<=2000;j++){
                //遍历每种可能的重量差
                int tmp1=-INF,tmp2=-INF;
                //分别记录当前柑橘放在第一堆或第二堆时
                //转移得来的新值,若无法转移则为-INF

                if(j+list[i]<=2000 && dp[i-1][j+list[i]+OFFSET]!=-INF){
                    //当状态可以由放在第一堆转移而来时
                    tmp1=dp[i-1][j+list[i]+OFFSET]+list[i];
                    //记录该转移值
                }

                if(j-list[i]>=-2000 && dp[i-1][j-list[i]+OFFSET]!=-INF){
                    //当状态可以由放在第二堆转移而来时
                    tmp2=dp[i-1][j-list[i]+OFFSET]+list[i];
                    //记录该转移值
                }

                if(tmp1 < tmp2){
                    tmp1=tmp2;
                }//取两者中较大的那个,保存至tmp1

                if(tmp1 < dp[i-1][j+OFFSET]){
                    //将tmp1与当前柑橘不放入任何堆即状态差
                    //不发生改变的原状态值比较,取较大的值保存至tmp1
                    tmp1=dp[i-1][j+OFFSET];
                }

                dp[i][j+OFFSET]=tmp1;
                //当前值状态保存为三个转移来源
                //转移得到的新值中最大的那个
            }
        }
        printf("Case %d:",++cas);//按题目要求输出
        if(dp[n][0+OFFSET]==0){
            puts(HaveZero==true ? "0" : "-1");
            //根据是否存在重量为0的柑橘输出0或-1
        }else{
            printf("%d\n",dp[n][0+OFFSET]/2);
            //否则输出dp[n][0]/2
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 贪心最佳优先搜索(Greedy Best-First Search)是一种启发式搜索算法,它通过在每个搜索步骤中优先考虑最有可能导致解决方案的节点,来寻找最优解。与广度优先搜索不同,贪心最佳优先搜索使用一个评估函数来衡量搜索状态的好坏,然后按照评估函数的值来选择下一个要扩展的节点。贪心最佳优先搜索通常用于解决状态空间较大,但具有良好的启发信息的问。 ### 回答2: 贪婪最优先搜索(GBFS)是一种启发式搜索算法,其基本思想是根据一定的启发函数,尽可能快地向目标状态方向搜索。它的搜索过程类似于最优先搜索,但是它不使用精确的代价函数,而是使用启发函数来估计从当前状态到目标状态的代价。 GBFS通过选择启发函数中具有最小估计代价的节点来扩展搜索树,因此它可能会快速地向目标状态移动。然而,由于它只考虑了估计代价,而没有考虑实际代价,因此它有可能陷入死胡同或者过早的终止搜索。 与最优先搜索类似,GBFS也具有一个开放列表,其中包含了待搜索的节点。通过比较启发函数的值,GBFS选择启发值最小的节点进行扩展。当搜索到达目标状态时,GBFS停止搜索。 与其他搜索算法相比,GBFS具有以下优缺点: 优点: 1. 它可以快速地向目标状态移动。 2. 它在内存和计算资源上比A*搜索更加高效。 缺点: 1. 它可能陷入局部最优解并忽略其他有可能更优的解。 2. 它无法保证找到最优解。 3. 它存在一个问,即当启发函数不准确时,可能会浪费大量的时间和计算资源搜索不必要的节点。 总的来说,GBFS对于大多数问而言是一个高效的搜索算法,但是它的表现取决于使用的启发函数的准确性。 ### 回答3: 贪婪最佳优先搜索(Greedy Best-First Search)是一种启发式搜索算法,它选取可行节点中最有希望的节点作为下一个扩展节点。在贪婪最佳优先搜索中,每个节点都被赋予一个估价函数的值,估价函数用于估计从当前节点到目标节点的最小代价或距离,该算法通常用于解决单目标问,如路径规划或机器人导航。 贪婪最佳优先搜索过程中,节点状态用一个优先队列来维护。节点的优先级由估价函数的值决定,即节点的估价函数值越小,则节点的优先级越高,队列中位置越靠前。这种贪心策略导致该算法的效率很高,但是不能保证它能找到全局最优解,它只能找到靠近目标的局部最优解。 贪婪最佳优先搜索的优点是速度快,但是它有缺陷。该算法只能找到越来越接近终点的节点,但是它不能保证一些没有被考虑的位于路径上的节点,这些节点可能会导致更短或更优的路径。因此,该算法只适用于简单问,但是在复杂问上找到最优解的准确性不可靠。 在实际问中,我们通常使用A*算法代替贪婪最佳优先搜索。A*算法既具备贪婪最佳优先搜索的速度和效率,又能优化贪婪算法的不足之处,它能确保找到最优解,并取得了广泛的应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值