目录

1 问题描述

2 解决方案

 


1 问题描述

问题描述
  给n个有序整数对ai bi,你需要选择一些整数对 使得所有你选定的数的ai+bi的和最大。并且要求你选定的数对的ai之和非负,bi之和非负。
输入格式
  输入的第一行为n,数对的个数
  以下n行每行两个整数 ai bi
输出格式
  输出你选定的数对的ai+bi之和
样例输入
5
-403 -625
-847 901
-624 -708
-293 413
886 709
样例输出
1715
数据规模和约定
  1<=n<=100
  -1000<=ai,bi<=1000

2 解决方案

本题主要考查动态规划思想的运用,下面的具体编码参考自文末参考资料1,我看了一下文中的讲解:

主要核心问题是:题目要求结果中所有ai之和非负,bi的和非负。

那么,首先,对输入数据处理一下,过滤掉所有ai + bi <= 0的数据对,对于剩下的数据对中,从第一个数据对开始,对于前i行数据,依次求取当ai的和从0到200000之间某一个值时,前i行中bi和的最大值。那么当i = n,即最后一行数据时,求取:当dp[i][j] >= 0(PS:bi的和非负)且j >= 100000(PS:ai的和非负),其中的dp[i][j]+j的最大值,即为最终结果。对于200000和100000的定义,详细参考代码注释哦。

有点遗憾的是,下面的代码在系统运行评分为91分...代码仅供参考

具体代码如下:

import java.util.Scanner;

public class Main {

    public int[][] dp = new int[105][200005];   

    public final static int t = 100001;  //ai或者bi所有输入项和最大为100*1000 = 100000
public final static int f = -200005; //ai+bi所有输入项和最小为100*-1000*2 = -200000 public int getMax(int a, int b) {
return a > b ? a : b;
}
/*
* dp[i][j]:i表示符合输入数据中ai + bi > 0的前i项,j表示这i项中ai的和
* dp[i][j]:其具体含义为当前i项ai和为j时,存放前i项中bi和的最大值
* 那么,可知共有n项满足ai + bi > 0,那么找出dp[n][j] + j最大值即为最终结果
*/
public void printResult(int[] A, int[] B, int len) {
for(int i = 0;i < len;i++) {
for(int j = -t;j < t;j++)
dp[i][j + t] = f; //初始化为题目所有输入数据和最小值,即前i项中bi的和
}
for(int i = 0;i < len;i++) {
//此处,使用t作为防止数组越界标准数,因为A[i]有可能小于0,而数组下标不可能为负数
dp[i][A[i] + t] = B[i]; //初始化当j = A[i] + t时,此时前i项B的和为B[i]
}
for(int i = 1;i < len;i++) {
for(int j = -t;j < t;j++) {
dp[i][j + t] = getMax(dp[i - 1][j + t], dp[i][j + t]);
if(j + t - A[i] < 0 || j + t - A[i] > 200001)
continue;
//此处判定,作为当j + t中不包含第i项A[i]时,更新当前最大值
dp[i][j + t] = getMax(dp[i][j + t], dp[i - 1][j + t - A[i]] + B[i]);
}
}
int result = f;
for(int i = 0;i < t;i++) {
//可知,最终的dp[i][j]中,其中j >= t,dp[i][j] >= 0,才符合对于sum(ai)和sum(bi)均不小于0的要求
if(dp[len - 1][i + t] >= 0)
result = getMax(result, dp[len - 1][i + t] + i);
}
if(result <= 0) {
System.out.println(0);
return;
}
System.out.println(result);
return;
} public static void main(String[] args) {
Main test = new Main();
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int len = 1;
int[] A = new int[n + 1];
int[] B = new int[n + 1];
for(int i = 1;i <= n;i++) {
int a = in.nextInt();
int b = in.nextInt();
if(a + b > 0) { //过滤掉所有a + b <= 0的数据对
A[len] = a;
B[len++] = b;
}
}
test.printResult(A, B, len);
}
}

 

上述错误修正如下:

错误在输入数据处理上:即注释中//过滤掉所有a + b <= 0的数据对,看到文末网友评论,我仔细想了一下数据处理的逻辑,发现如果过滤掉所有a + b<=0的数据对,会过滤掉其中a > 0或者b >  0的情形,导致在求ai的和出现误差,以及bi的和出现误差。(PS:即如果ai+a,其中a > 0,那么遇到下一个数据对时,其可以接纳的数据对就会增加,同理,对于bi也一样。)

现在有两种方案:

(1)把过滤条件修改为如下:if(a < 0 && b < 0)  continue;

(2)不对输入数据进行过滤处理,直接计算处理所有输入数据对。

验证结果如下(PS:在蓝桥杯练习系统中评分均为100分):

修改后代码:

import java.util.Scanner;

public class Main {

    public int[][] dp = new int[105][200005];   

    public final static int t = 100001;  //ai或者bi所有输入项和最大为100*1000 = 100000
public final static int f = -200005; //ai+bi所有输入项和最小为100*-1000*2 = -200000 public int getMax(int a, int b) {
return a > b ? a : b;
}
/*
* dp[i][j]:i表示符合输入数据中ai + bi > 0的前i项,j表示这i项中ai的和
* dp[i][j]:其具体含义为当前i项ai和为j时,存放前i项中bi和的最大值
* 那么,可知共有n项满足ai + bi > 0,那么找出dp[n][j] + j最大值即为最终结果
*/
public void printResult(int[] A, int[] B, int len) {
for(int i = 0;i < len;i++) {
for(int j = -t;j < t;j++)
dp[i][j + t] = f; //初始化为题目所有输入数据和最小值,即前i项中bi的和
}
for(int i = 0;i < len;i++) {
//此处,使用t作为防止数组越界标准数,因为A[i]有可能小于0,而数组下标不可能为负数
dp[i][A[i] + t] = B[i]; //初始化当j = A[i] + t时,此时前i项B的和为B[i]
}
for(int i = 1;i < len;i++) {
for(int j = -t;j < t;j++) {
dp[i][j + t] = getMax(dp[i - 1][j + t], dp[i][j + t]);
if(j + t - A[i] < 0 || j + t - A[i] > 200001)
continue;
//此处判定,作为当j + t中不包含第i项A[i]时,更新当前最大值
dp[i][j + t] = getMax(dp[i][j + t], dp[i - 1][j + t - A[i]] + B[i]);
}
}
int result = f;
for(int i = 0;i < t;i++) {
//可知,最终的dp[i][j]中,其中j >= t,dp[i][j] >= 0,才符合对于sum(ai)和sum(bi)均不小于0的要求
if(dp[len - 1][i + t] >= 0)
result = getMax(result, dp[len - 1][i + t] + i);
}
if(result <= 0) {
System.out.println(0);
return;
}
System.out.println(result);
return;
} public static void main(String[] args) {
Main test = new Main();
Scanner in = new Scanner(System.in);
//System.out.println("请输入:");
int n = in.nextInt();
int len = 1;
int[] A = new int[n + 1];
int[] B = new int[n + 1];
for(int i = 1;i <= n;i++) {
int a = in.nextInt();
int b = in.nextInt();
// if(a < 0 && b < 0)
// continue;
// if(a + b > 0) { //过滤掉所有a + b <= 0的数据对
A[len] = a;
B[len++] = b;
// }
}
test.printResult(A, B, len);
}
}

参考资料:

1.蓝桥杯 算法提高 求最大值

算法笔记_096:蓝桥杯练习 算法提高 求最大值(Java)的更多相关文章

  1. 算法笔记_107:蓝桥杯练习 算法提高 学霸的迷宫(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗.但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要 ...

  2. 算法笔记_091:蓝桥杯练习 递推求值(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 已知递推公式: F(n, 1)=F(n-1, 2) + 2F(n-3, 1) + 5, F(n, 2)=F(n-1, 1) + 3F(n- ...

  3. 算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是 ...

  4. 算法笔记_081:蓝桥杯练习 算法提高 矩阵乘法(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 有n个矩阵,大小分别为a0*a1, a1*a2, a2*a3, ..., a[n-1]*a[n],现要将它们依次相乘,只能使用结合率,求最 ...

  5. 算法笔记_106:蓝桥杯练习 算法提高 周期字串(Java)

    目录 1 问题描述 2 解决方案 2.1 第一印象解法(80分) 2.2 借鉴网友解法(100分)   1 问题描述 问题描述 右右喜欢听故事,但是右右的妈妈总是讲一些“从前有座山,山里有座庙,庙里有 ...

  6. 算法笔记_102:蓝桥杯练习 算法提高 快乐司机(Java)

    目录 1问题描述 2 解决方案   1 问题描述 问题描述 "嘟嘟嘟嘟嘟嘟 喇叭响 我是汽车小司机 我是小司机 我为祖国运输忙 运输忙" 这是儿歌“快乐的小司机”.话说现在当司机光 ...

  7. 算法笔记_099:蓝桥杯练习 算法提高 排列数(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 0.1.2三个数字的全排列有六种,按照字母序排列如下: 012.021.102.120.201.210 输入一个数n 求0~9十个数的全排 ...

  8. 算法笔记_105:蓝桥杯练习 算法提高 上帝造题五分钟(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 第一分钟,上帝说:要有题.于是就有了L,Y,M,C 第二分钟,LYC说:要有向量.于是就有了长度为n写满随机整数的向量 第三分钟,YUHC ...

  9. 算法笔记_104:蓝桥杯练习 算法提高 新建Microsoft Word文档(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 L正在出题,新建了一个word文档,想不好取什么名字,身旁一人惊问:“你出的题目叫<新建Microsoft Word文档>吗? ...

随机推荐

  1. HRBUST 1213 单词接龙

    暴力搜索. 按照能配对的关系建立有向边,然后暴力搜索. #include<cstdio> #include<cstring> #include<cmath> #in ...

  2. 【记忆化搜索】codevs2823锁妖塔

    [codevs2823]锁妖塔 Description 琐妖塔会在一会儿后倒塌.大量妖魔涌出塔去,塔内的楼梯都挤满了人(哦,错了,是妖),(那他们怎么不飞下去--)要求是,景天一行一定要下塔,琐妖塔一 ...

  3. [CF985G]Team Players

    题意:给出一个图,求$\sum\limits_{\substack{i\lt j\lt k\\\nexists(i,j),(j,k),(i,k)}}Ai+Bj+Ck$ 挺好的一道题==,就是稍微毒了点 ...

  4. 【枚举】【SPFA】Urozero Autumn Training Camp 2016 Day 5: NWERC-2016 Problem I. Iron and Coal

    那个人派出的队伍的行走的路径一定前半程是重合的,后半程分叉开来. 于是预处理每个点离1号点的最短路,到最近的铁的最短路,到最近的煤的最短路.(三次BFS / SPFA)然后枚举分岔点,尝试更新答案即可 ...

  5. python基础之数据类型之元组和字典

    四.元组 1.用途:元组是不可变的列表,能存多个值,但只能取,不能改 2.定义:name = (‘alex’, ’egon’, ‘wxx’) 在()内用,分割开,可存放任意类型的值 强调:x = (‘ ...

  6. Java学习笔记(8)

    static修饰方法(静态的成员方法): 访问方式: 可以使用对象进行访问                   对象.静态函数名(): 可以使用类名进行访问                   类名. ...

  7. Problem B: 年龄问题

    #include<stdio.h> int xx(int n,int m,int k) { int a; ) { a=k; } else { a=xx(n-,m,k)+m; } retur ...

  8. canvas之arcTo

    arc与arcTo,从名字都能看出来相似.arcTo也是画曲线的方法,而且他画出的曲线也是正圆的一段弧线.但他的参数和arc简直是不共戴天~ ctx.arcTo(x1,y1,x2,y2,radius) ...

  9. openstack-cinder-netapp

    https://communities.netapp.com/docs/DOC-31749 http://docs.openstack.org/havana/config-reference/cont ...

  10. CentOS 6.9下KVM虚拟机快照创建、删除、恢复(转)

    使用文件快照的方式实现文件备份,但单说快照(snapshot)的话,他是某一时间点(版本)你能看到的该时间点备份文件状态的全貌,通过文件的快照(全貌)你能恢复到特定时间点(版本)的文件状态. 创建虚拟 ...