2 条题解

  • 1
    @ 2026-4-2 11:02:11

    题目描述:

    有两个序列 a1, a2, ..., an 和 b1, b2, ..., bm,以及一个总预算 k。

    你可以按顺序从两个序列的开头取数(每次只能取当前序列的第一个元素),取走的数会累加其值到总花费中。

    问在总花费不超过 k 的前提下,最多能取多少个数。

    一个容易想到的贪心策略是:

    用两个指针 i 和 j 分别指向 a 和 b 的当前开头。

    每次比较 a[i] 和 b[j],如果 a[i] > b[j] 就取 b[j](因为代价更小),否则取 a[i]。

    这种贪心在某些情况下会出错。

    例如:

    n=4, m=3, k=6

    a = [1, 4, 1, 1]

    b = [3, 3, 5]

    贪心过程:先取 a[1]=1,再取 b[1]=3,总花费 1+3=4,此时 a 指针指向 4,b 指针指向 3。下一步无论取 a[2]=4 还是 b[2]=3 都会使花费超过 k,最终只能取 2 个数。

    但事实上,我们完全可以跳过 b 而全部取 a:取 a[1]=1,a[2]=4,a[3]=1,总花费 1+4+1=6,可以取 3 个数。

    贪心漏掉了这种情况,因此不能直接贪心。

    正确解法:枚举 + 二分

    由于两个序列都是按顺序取,如果我们决定取 x 个数,那么必然是从 a 中取 i 个,从 b 中取 x-i 个,并且 0 <= i <= n,0 <= x-i <= m。

    我们可以预处理 a 和 b 的前缀和:

    sa[i] = a1 + a2 + ... + ai

    sb[j] = b1 + b2 + ... + bj

    那么取 i 个 a 和 j 个 b 的总花费为 sa[i] + sb[j]。

    对于给定的 x,我们只需检查是否存在 i 属于 [0, x] 满足 i <= n 且 x-i <= m,且 sa[i] + sb[x-i] <= k。

    如果存在,说明 x 是可行的。

    由于 x 越大越难满足,我们可以对 x 进行二分查找,找到最大的可行 x。

    代码实现(C++):

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    
    int n, m, k;
    int a[200100], b[200100], sa[200100], sb[200100];
    
    bool check(int x) {
        // 枚举从 a 中取 i 个,从 b 中取 x-i 个
        for (int i = 0; i <= x; i++) {
            if (i > n || x - i > m) continue; // 越界跳过
            if (sa[i] + sb[x - i] <= k) return true;
        }
        return false;
    }
    
    signed main() {
        cin >> n >> m >> k;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            sa[i] = sa[i - 1] + a[i];
        }
        for (int j = 1; j <= m; j++) {
            cin >> b[j];
            sb[j] = sb[j - 1] + b[j];
        }
    
        int l = -1, r = n + m + 1, mid;
        while (l + 1 < r) {
            mid = (l + r) / 2;
            if (check(mid)) l = mid;
            else r = mid;
        }
        cout << l << endl;
        return 0;
    }
    

    时间复杂度:

    总复杂度 O((n+m) log(n+m)),可以通过本题。

    总结:

    直接贪心(每次取较小值)是错误的,因为序列并非有序,可能为了取后面的较小值而放弃前面的大值反而更优。

    正确做法是枚举从两个序列中各取多少个数,利用前缀和快速计算总花费,并二分答案找到最大可行数量。

    这种“枚举分配”的思路在类似“从两个序列开头按顺序取数”的问题中非常常用。

    • 0
      @ 2026-4-4 17:01:24

      import java.util.Scanner; public class Main { public static void main(String[] args) {

          Scanner sc = new Scanner(System.in);
          int n=sc.nextInt();
          int m=sc.nextInt();
          long k=sc.nextLong();
          int[]a=new int[n+2];
          int[]b=new int[m+2];
          long[]sumA=new long[n+2];
          long[]sumB=new long[m+2];
          for(int i=1;i<=n;i++) {//前缀和
          	a[i]=sc.nextInt();
          	sumA[i]=a[i]+sumA[i-1];
          }
          for(int i=1;i<=m;i++) {
          	b[i]=sc.nextInt();
          	sumB[i]=b[i]+sumB[i-1];
          }
                
          
          int max=0;
          for(int i=0;i<=n;i++) {//遍历sumA
          	if(sumA[i]>k)break;
          	
          	int l=0,r=m,j=0;
          	while(l<=r) {//二分找出当前最大sumB
          		int mid=l+(r-l)/2;
          		if(sumA[i]+sumB[l]<=k) {
          			l=mid+1;
          			j=mid;
          		}else {
          			r=mid-1;
          		}
          	}
          	max=Math.max(max, i+j);
          }
          System.out.println(max);
      }   
      

      }

      • 1

      信息

      ID
      385
      时间
      1000ms
      内存
      256MiB
      难度
      7
      标签
      递交数
      438
      已通过
      91
      上传者