首页 > 试题广场 >

倒卖战利品

[编程题]倒卖战利品
  • 热度指数:3923 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
在游戏中,击败魔物后,薯队长获得了N件宝物,接下来得把这些宝物卖给宝物回收员来赚点小钱。这个回收员有个坏毛病,每次卖给他一件宝 物后,之后他就看不上比这件宝物差的宝物了。在这个世界中,衡量宝物的好坏有两个维度,稀有度X和实用度H,回收员在回收一个宝物A 后,下一个宝物的稀有度和实用度都不能低于宝物A。那么薯队长如何制定售卖顺序,才能卖给回收员宝物总个数最多。 

输入描述:
第一行一个正整数N。 接下来N行。每行两个整数分别表示X    和    H X1    H1 X2    H2 … XN    HN
输入限制: 对于70%的数据: 
0<N<10^4 
0<Xi<10^6 
0<Hi<10^6 
100%的数据:
0<N<10^6
0<Xi<10^6 
0<Hi<10^6


输出描述:
一个整数,表示最多可以卖出的宝物数
示例1

输入

4
3 2
1 1 
1 3
1 2

输出

3
按照一个维度排序后按照另一个维度寻找最长增加子序列即可,这个是>=的比较简单一点,注意不能用O(n2),要二分查找优化

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[][] ans = new int[n][2];
        for(int i=0;i<n;i++){
            ans[i][0] = scanner.nextInt();
            ans[i][1] = scanner.nextInt();
        }
        Arrays.sort(ans,(a,b)->a[0]!=b[0]?a[0]-b[0]:a[1]-b[1]);
        int[] arr = new int[n];
        for(int i=0;i<n;i++)arr[i] = ans[i][1];
        System.out.println(LIS(arr));
    }
    public static int LIS(int[] arr){
        int[] dp = new int[arr.length];
        int res = 0;
        for(int num:arr){
            int l = 0,r = res;
            while (l<r){
                int m = (l+r)/2;
                if(dp[m]<num)l = m+1;
                else r = m;
            }
            dp[l] = num;
            if(l==res)res++;
        }
        return res;
    }
}

发表于 2020-06-07 13:42:37 回复(7)


import sys
class Z:
    def __init__(self,x,h):
        self.x = x
        self.h = h
    def __lt__(self,obj):
        if self.x != obj.x:
            return self.x < obj.x
        else:
            return self.h <obj.h

N = int(input())
z_list =[]
for i in range(N):
    x,h = map(int,sys.stdin.readline().strip().split())
    z_list.append(Z(x,h))

z_list.sort()
arr = [item.h for item in z_list]

def binary_search(arr1,start,end,target):
    if start < end:
        mid =(start+end)//2
        if arr1[mid]< target:#去找大于等于的位置
            return binary_search(arr1,mid+1,end,target)
        else:
            return binary_search(arr1,start,mid,target)
    else:
        return start


len_list=[arr[0]]
for index in range(1,len(arr)):
    item = arr[index]
    pos = binary_search(arr1 =len_list,start=0,end=len(len_list),target= item)
   
    if len(len_list)==pos:
        len_list.append(item)
    else:
        len_list[pos]=item
print(len(len_list))

发表于 2020-06-08 17:17:05 回复(1)
import java.util.Arrays;
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n =sc.nextInt();
        int[][] arr=new int[n][2];
        for(int i=0;i<n;i++){
            arr[i][0]=sc.nextInt();
            arr[i][1]=sc.nextInt();
        }
        Arrays.sort(arr,(a,b)->a[0]==b[0] ? a[1]-b[1]:a[0]-b[0]);
        //用lambda改写构造器,按照第一个元素大小升序,相等则按照第二个元素倒序
        //a[1]-b[1]>0,则a[1],b[1]交换位置
        
        int[] nArr=new int[n];
        for(int i=0;i<n;i++) nArr[i]=arr[i][1];
        System.out.println(nout(nArr));
    }
        public static int out(int[] nArr){//寻找最长增子序列动态规划
            int result=0;
            int len=nArr.length;
            int[] dp=new int[len];
            //dp[i]代表以nArr[i]结尾的上升子序列长度!!!
            Arrays.fill(dp,1);
            for(int i=1;i<len;i++){
                for(int j=0;j<i;j++){
                    if(nArr[i]>nArr[j])
                        dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            for(int i=0;i<len;i++){
                result=Math.max(result,dp[i]);
            }
            return result;
        }    
    public static int nout(int[] nArr){//优化后
          int len = nArr.length;
        if (len <= 1) {
            return len;
        }

        // tail 数组的定义:长度为 i + 1 的上升子序列的末尾最小是几
        int[] tail = new int[len];
        // 遍历第 1 个数,直接放在有序数组 tail 的开头
        tail[0] = nArr[0];
        // end 表示有序数组 tail 的最后一个已经赋值元素的索引
        int end = 0;

        for (int i = 1; i < len; i++) {
            // 【逻辑 1】比 tail 数组实际有效的末尾的那个元素还大
            if (nArr[i] > tail[end]) {
                // 直接添加在那个元素的后面,所以 end 先加 1
                end++;
                tail[end] = nArr[i];
            } else {
                // 使用二分查找法,在有序数组 tail 中
                // 找到第 1 个大于等于 nums[i] 的元素,尝试让那个元素更小
                int left = 0;
                int right = end;
                while (left < right) {
                    // 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解
                    // int mid = left + (right - left) / 2;
                    int mid = left + ((right - left) >>> 1);
                    if (tail[mid] < nArr[i]) {
                        // 中位数肯定不是要找的数,把它写在分支的前面
                        left = mid + 1;
                    } else {
                        right = mid;
                    }
                }
                // 走到这里是因为 【逻辑 1】 的反面,因此一定能找到第 1 个大于等于 nums[i] 的元素
                // 因此,无需再单独判断
                tail[left] = nArr[i];
            }
            // 调试方法
            // printArray(nums[i], tail);
        }
        // 此时 end 是有序数组 tail 最后一个元素的索引
        // 题目要求返回的是长度,因此 +1 后返回
        end++;
        return end;
    }

}


//out是动态规划,通过率只有80%
//nout优化后,通过了
借鉴了楼上大哥的
编辑于 2020-08-28 21:39:57 回复(0)
对x维进行排序,对y求最长上升子序列。
这道题题目描述有问题:题目中说的是不低于,含义应该是大于等于,而实际答案却是大于!

n=int(input())
a=[[int(i) for i in input().split()]for _ in range(n)]

a=sorted(a)
a=[i[1]for i in a]
f = [0]*len(a)
ans = 0
for i in a:
    l = 0
    r = ans
    while l < r:
        m = (l + r) >> 1
        if f[m] < i:
            l = m + 1
        else:
            r = m
    f[l] = i
    if l == ans:
        ans += 1
print(ans)


发表于 2020-06-23 14:30:45 回复(0)
牛客网 的输入输出 真的 蛮搞人心态的 调整输入输出时间 跟 写代码时间一样长 做一道题花两道题时间😂😂
var num = readline();
var arr= [];
var n = null;
while(n = readline()){
    n=n.split(" ").map(item => {
        return Number(item)
    })
    arr.push(n)
}
arr.sort((d1, d2) => {
        return d1[0] != d2[0] ? d1[0] - d2[0] : d1[1] - d2[1]
      });
function LIS(num, arr) {
      var temp = [];
      for (var i = 0; i < num; i++) {
        temp.push(arr[i][1]);
      }
      let newArr = new Array(num);
      newArr[0] = temp[0]
      let end = 0;
      for (var k = 0; k < num; k++) {
        if (temp[k] > newArr[end]) {
          end++;
          newArr[end] = temp[k];
        } else {
          let left = 0 ;
          let right = end ;
          while(left < right){
            let mid = left + ((right - left) >> 1);
            if(newArr[mid] < temp[k]){
              left = mid + 1;
            } else {
              right = mid;
            }
          }
          newArr[left] = temp[k]
        }
      }
      return end + 1
    }
      console.log(LIS(num, arr)) ;


发表于 2020-07-09 21:26:13 回复(3)

分析

  • 思路 先对数组排序(sort函数会同时对两个维度排序,第一个维度相同时会比较第二个维度),然后在另一个维度上搜索最长上升子序列。
 input: [[32],[11],[13],[12]]

sorted(input):[[11],[12],[13],[32]]
  • 时间复杂度的限制:在找最长上升子序列时不能使用DP方法(O(N^2)),考虑通过二分查找来找到LIS。
  • LIS的二分查找算法参考: leetcode 300.最长上升子序列
    • 构造单调上升数组res,对原数组nums逐个遍历:
      • 如果nums[i]>res[-1],说明满足上升条件,将其插入数组中;
      • 否则,通过二分查找res数组中刚好比它大的值并进行替换。当完成多次替换后该数组的最大值会减小,从而能向res中添加一些原数组中较小的值。
    • 计算数组的长度得到LIS的最大值。
  • 注意二分搜索时的边界以及返回值选择
def binary_search(nums,left,right,val):
    mid = 0
    while left < right:
        mid = (left+right) // 2
        if val > nums[mid]:
            left = mid +1
        else:
            right = mid
    return left

def LIS(N,prices):
    res = []
    for i in range(N):
        if not res or prices[i] > res[-1]:
            res.append(prices[i])
        else:
            idx = binary_search(res, 0, len(res), prices[i])
            res[idx] = prices[i]
    return len(res)

def main():
    N = int(input())
    prices = []
    for i in range(N):
        prices.append(list(map(int,input().split())))
    prices.sort()
    h = [a[1] for a in prices]
    return LIS(N,h)

print(main())
编辑于 2020-07-06 22:30:59 回复(3)
力扣面试题:马戏团人搭。最长递增子序列问题
n = int(input())
nums = []
for _ in range(n):
    nums.append(list(map(int, input().split())))
nums.sort(key = lambda x:[x[0], x[1]])
res = []
for i in range(n):
    if not res&nbs***bsp;nums[i][1] >= res[-1]:
        res.append(nums[i][1])
    else:
        l, r = 0, len(res)-1
        while l <= r:
            mid = (l+r)//2
            if res[mid] < nums[i][1]:
                l = mid + 1
            elif res[mid] >= nums[i][1]:
                r = mid - 1
        res[l] = nums[i][1]
print(len(res))


发表于 2020-09-07 15:01:26 回复(0)
有详解。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

static bool cmp(vector<int> & a, vector<int> &b)
{
    return a[0]<b[0]||(a[0]==b[0]&&a[1]<b[1]);
}

int main()
{
    int N;
    cin>>N;
    int h, x;
    vector<vector<int>> nums;
    while(cin>>h>>x)
    {
        nums.push_back({h, x});
    }
    int ret=0;
    sort(nums.begin(), nums.end(), cmp);
    vector<int> dp;
    dp.push_back(nums[0][1]);
    for(int i=1;i<nums.size();i++)
    {
        if(nums[i][1]>=dp.back())    {dp.push_back(nums[i][1]);}
        else {int idx = lower_bound(dp.begin(), dp.end(), nums[i][1])-dp.begin(); dp[idx]=nums[i][1];}    //lower_bound:找到地一个大于或等于该数的位置
    }
    cout<<dp.size();
    return 0;
}


发表于 2021-01-06 21:33:37 回复(0)

这种思路比较容易理解:但是只有60%通过率,时间复杂度太高了

import sys

def helper(res):
    res.sort()
    nums = []
    for i in res:
        nums.append(i[1])
    #对nums进行一次最长上升子序列即可
    dp = [1]*len(nums)

    for i in range(len(nums)):
        for j in range(i):
            if nums[j]<nums[i]:
                dp[i] = max(dp[i],dp[j]+1)
    return max(dp)

    pass
if __name__ == "__main__":
    n = int(sys.stdin.readline().strip())
    res = []
    for i in range(n):
        line = sys.stdin.readline().strip()
        values = list(map(int, line.split(' ')))
        res.append(values)
    out = helper(res)
    print(out)
编辑于 2020-07-04 15:16:51 回复(0)
这题描述有错误!要求应该是x不小于之前的,h一定要大于之前的。用一维Binary Indexed Tree加上DP,全部通过。

class BIT:
    def __init__(self,bound):
        self.bound = bound
        self.tree = {}
    def updateAt(self,i,val):
        while i <= self.bound:
            if i not in self.tree:
                self.tree[i] = val
            if self.tree[i] > val:
                break
            self.tree[i] = max(self.tree[i],val)
            i += i&-i
    def maxUntil(self,i):
        res = 0
        while i>0:
            if i in self.tree:
                res = max(res,self.tree[i])
            i -= i&-i
        return res
 
if __name__=="__main__":
    n = int(raw_input())
    bound,items = n,[]
    hs = []
    for i in xrange(n):
        x,h = map(int,raw_input().split())
        items.append((x,h))
        hs.append(h)
    hs.sort()
    hmap = {}
    for i,h in enumerate(hs):
        if h not in hmap:
            hmap[h] = i+1
    items.sort()
    bit = BIT(bound)
    res = 0
    for x,h in items:
        preMax = bit.maxUntil(hmap[h]-1)
        bit.updateAt(hmap[h],preMax+1)
        res = max(res,preMax+1)
    print res


发表于 2020-06-29 12:51:09 回复(2)
以为这个题O(n2)复杂度的通过不了,写了一个过程超级复杂的,但只通过了一个案例,笑死!
发表于 2023-07-24 13:16:24 回复(0)
题目描述应该有问题应该是两个维度不小于且至少一个大于,或者题目应该加上任意两个宝物的二维不相等。
各位的代码可以试试这个算例:
4
1 5
1 10
2 5
2 5
输出是2
n = int(input())
goods = []
for i in range(n):
    goods.append(list(map(int,input().split())))
goods.sort(key = lambda x:(x[0],x[1]))
stack = []
for i in range(len(goods)):
    # print(stack)
    if not stack or stack[-1]<=goods[i][1]:
        stack.append(goods[i][1])
        continue
    for k in range(len(stack)):
        if stack[k] < goods[i][1]:
            continue
        else:
            stack[k] = goods[i][1]
            break
print(len(stack))


编辑于 2022-10-27 20:32:10 回复(0)
def binaysearch(nums,left,right,val):
    while left < right:
        mid = (left + right) // 2
        if nums[mid] >= val:
            right = mid
        elif nums[mid] < val:
            left = mid + 1
    return left
            
def ans(nums,num):
    nums = sorted(nums,key = lambda x:(x[0],x[1]))
    temp = [0]*num
    for i in range(num):
        temp[i] = nums[i][1]
    res = []
    for i in range(num):
        if res == []&nbs***bsp;res[-1] < temp[i]:
            res.append(temp[i])
        else:
            idx = binaysearch(res, 0, len(res), temp[i])
            res[idx] = temp[i]
    return len(res)
   
if __name__ == "__main__":
    num = int(input()) #宝物的数量
    nums = []
    for i in range(num):
        nums.append(list(map(int,input().split())))
    print(ans(nums,num))


发表于 2022-08-06 15:44:07 回复(0)
//参考大佬们的方法,注意这题的最长子序列需要>=而不是>
import java.util.*;
public class Main {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int n = sc.nextInt();
            int[][] nums = new int[n][2];
            for (int i = 0; i < n; i++) {
                nums[i][0] = sc.nextInt();
                nums[i][1] = sc.nextInt();
            }
            sc.close();

            Arrays.sort(nums, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);

            int[] f = new int[n];
            f[0] = nums[0][1];
            int idx = 0;

            for (int i = 1; i < n; i++) {
                if (nums[i][1] >= f[idx]) {
                    f[++idx] = nums[i][1];
                } else {
                    int lo = 0, hi = idx;
                    while (lo < hi) {
                        int m = (hi - lo) / 2 + lo;
                        if (f[m] < nums[i][1]) {
                            lo = m + 1;
                        } else {
                            hi = m;
                        }
                    }
                    f[lo] = nums[i][1];
                }
            }
            System.out.println(idx + 1);
        }
    }

发表于 2022-04-10 10:27:32 回复(0)
先排序,按照稀有度递增,稀有度相同的情况下,按照实用度递增,最后对实用度进行最长递增子序列求解(dp + 二分)
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[][] goods = new int[n][2];
        for(int i = 0; i < n; i++) {
            goods[i][0] = in.nextInt();
            goods[i][1] = in.nextInt();
        }
        System.out.println(saleGoods(goods));
    }
     
    private static int saleGoods(int[][] goods) {
        int len = goods.length;
        Arrays.sort(goods, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] == o2[0] ? o1[1] - o2[1] : o1[0] - o2[0];
            }
        });
         
        int[] nums = new int[len];
        for(int i = 0; i < len; i++) {
            nums[i] = goods[i][1];
        }
         
        return lengthOfLIS(nums);
    }
     
    private static int lengthOfLIS(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len];
        int maxLen = 0;
        for(int num : nums) {
            int left = 0, right = maxLen;
            while(left < right) {
                int mid = (left + right) >> 1;
                if(num > dp[mid]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
            dp[left] = num;
            if(right == maxLen) {
                maxLen++;
            }
        }
        return maxLen;
    }
}


发表于 2021-03-25 20:06:50 回复(0)
说明:纠正题意,x大于等于,H要大于。不然通不过
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
    int n;
    cin>>n;
    vector<vector<int>> goods(n,vector<int>(2,0));
    for(int i=0;i<n;i++)
    {
        cin>>goods[i][0];
        cin>>goods[i][1];
    }
    //对X升序排序
    if(n<=0) return 0;
    sort(goods.begin(),goods.end(),[](vector<int>& a,vector<int>& b){return a[0]!=b[0]?(b[0]-a[0]>0):(b[1]-a[1]>0);});
    //寻找最长上升子序列
    vector<int> d(n+1,0);
    int len=0;
    for(int i=1;i<n;i++)
    {
    int hig=len;
    int low=0;
    int mid=(low+hig)/2;
    while(low<hig)
    {
        mid=(low+hig)/2;
        if(d[mid]<goods[i][1])
            low=mid+1;
        else
            hig=mid; 
    }
      d[low]=goods[i][1];
      if(len==low) len++;
    }
    cout<<len<<endl;
    return 0;
}


发表于 2020-12-14 19:22:44 回复(0)
“下一个宝物的稀有度和实用度都不能低于宝物A”对应的条件应该是:
下一件宝物的稀有度和实用度  大于等于   宝物A,
评测只通过大于的(排序后的上升子序列)
讨论里的解析也是有问题的
发表于 2020-09-28 15:14:15 回复(0)
n = int(input())
WoW_goods = []
for i in range(n):
    goods = list(map(int, input().split()))
    WoW_goods.append(goods)
# print(WoW_goods)
WoW_goods.sort(key=lambda x: [x[0], x[1]])
# print(WoW_goods)
H = [WoW[1] for WoW in WoW_goods]
# print(H)
LIS = [H[0]]
import bisect
for i in range(1, n):
    idx = bisect.bisect_left(LIS, H[i])
    if idx==len(LIS):
        LIS.append(H[i])
    else:
        LIS[idx] = H[i]
print(len(LIS))

发表于 2020-08-29 18:48:33 回复(0)
个人感觉题目后台数据有问题。以下数据输出应该为8,实际上,我过的的程序输出为5。
10
1 1
1 5
1 5
1 6
1 7
2 5
2 5
2 5
2 5
2 5

附上过的程序代码和我觉得应该正确的代码。
这个是过的代码:
#include <bits/stdc++.h>
using namespace std;

struct node{
    int x;
    int h;
    bool operator < (const node& ch)const{
        if(x == ch.x) return h < ch.h;
        return x < ch.x;
    }
};

int main(){
    int n;
    cin>>n;
    node N[n];
    int s[n];
    int cnt = -1;
    for(int i = 0; i < n; i++) cin>>N[i].x>>N[i].h;
    sort(N, N+n);
    int l, r, mid;
    for(int i = 0; i < n; i++){
        if(cnt == -1 || s[cnt] <= N[i].h){
            s[++cnt] = N[i].h;
            continue;
        }
        l = 0;
        r = cnt;
        while(l <= r){
            mid = l + (r - l) / 2;
            if(s[mid] < N[i].h){
                l = mid + 1;
            }
            else {
                r = mid - 1;
            }
        }
        s[l] = N[i].h;
    }
    cout<<cnt+1<<endl;
    return 0;
}
这个为应该正确的代码(差别在二分中的第31行):
#include <bits/stdc++.h>
using namespace std;

struct node{
    int x;
    int h;
    bool operator < (const node& ch)const{
        if(x == ch.x) return h < ch.h;
        return x < ch.x;
    }
};

int main(){
    int n;
    cin>>n;
    node N[n];
    int s[n];
    int cnt = -1;
    for(int i = 0; i < n; i++) cin>>N[i].x>>N[i].h;
    sort(N, N+n);
    int l, r, mid;
    for(int i = 0; i < n; i++){
        if(cnt == -1 || s[cnt] <= N[i].h){
            s[++cnt] = N[i].h;
            continue;
        }
        l = 0;
        r = cnt;
        while(l <= r){
            mid = l + (r - l) / 2;
            if(s[mid] <= N[i].h){
                l = mid + 1;
            }
            else {
                r = mid - 1;
            }
        }
        s[l] = N[i].h;
    }
    cout<<cnt+1<<endl;
    return 0;
}


编辑于 2020-07-28 14:01:14 回复(2)
n = int(input())
places = [ list(map(int,input().split())) for _ in range(n)]
places.sort(key=lambda l:(l[0],l[1]),reverse=False) # print(places) dp = [1] for i in range(1,len(places)):
    max_dp = 0  for j in range(0,i): if places[i][1] > places[j][1] and dp[j]>max_dp:
            max_dp = dp[j]
    dp.append(max_dp+1)
max_dp = 0 for i in dp: if i>max_dp:
        max_dp = i print(max_dp)
发表于 2020-06-22 09:35:24 回复(0)