4 条题解

  • 0
    @ 2025-11-27 14:44:28
    #include <bits/stdc++.h>
    using namespace std;
    
    int main(){
        int n,m;
        cin>>n>>m;
        // int t[m]={0};//潜在风险:不建议用变量作为数组的长度
        // int w[n]={0};//潜在风险:不建议用变量作为数组的长度
        int t[101]={0};
        int w[10001]={0};
        for(int i=0;i<n;i++){
            cin>>w[i];
        }
        //开始第一批m个人接水
        for(int j=0;j<m;j++){
            t[j]=w[j];
        }
        //循环条件:下一个接水的人去找位置接水
        for(int k=m;k<n;k++){//注意下标从m开始,因为前面m个是0~m-1
            //准备接水,要找那个水龙头是时间最小的
            int mint=INT_MAX;
            int minidx=0;
            for(int i=0;i<m;i++){
                if(t[i]<mint){
                    minidx=i;
                    mint=t[i];//不要写成w[i]  所以数组变量名写成time[i]更不容易出错
                }
            }
            //找到最小的水龙头位置了minidx
            t[minidx]+=w[k];//去接水,更新时间
        }
        //全部人都接完了,找最大的水龙头时间
        int maxt=0;
        for(int i=0;i<m;i++){
            if(t[i]>maxt){
                maxt=t[i];
            }
        }
        cout<<maxt<<endl;
    }
    
    • 0
      @ 2025-11-27 14:44:10

      P1190 这道题(NOIP 2010 普及组 T2)是一道非常经典的模拟题,同时包含了一点点贪心的思想。题目描述得很长,但其实核心逻辑就像我们在超市排队结账一样。

      为了帮助你独立解出这道题,我为你提供以下几个核心思路提示:

      1. 核心模型:水龙头数组

      想象一下,你有 mm 个水龙头。我们可以用一个长度为 mm 的数组(比如叫 time[m])来维护每一个水龙头当前还需要工作多长时间,或者理解为每一个水龙头将在第几秒结束当前任务

      • 初始状态:前 mm 个同学一来就占满了 mm 个龙头。所以,数组的初始值就是前 mm 个同学的接水量。

      2. 关键逻辑:谁最先接完?

      当第 m+1m+1 个同学(也就是排队等待的第一位)准备去接水时,他会去哪一个龙头呢?

      • 必然是去目前最快空闲下来的那个龙头!
      • 怎么找? 你需要在 time 数组里找到一个最小值。假设第 kk 号龙头的时间最少,说明它最先空出来。

      3. 状态更新:接力

      当第 kk 号龙头空出来后,排队的这位同学立刻补上去。

      • 这对 time[k] 会产生什么变化?
      • 原本的时间加上新同学需要的时间,就是这个龙头下一次空闲的时间。
      • 公式:time[min_index] += w[next_student]

      4. 循环处理

      你只需要写一个循环,从第 m+1m+1 个同学开始,直到第 nn 个同学:

      1. 找到当前 mm 个龙头中数值最小的那个(也就是最早结束的)。
      2. 把当前同学的接水量到那个龙头的数值上。

      5. 最终答案:什么时候全部结束?

      当所有 nn 个同学都安排好之后,time 数组里记录了每个龙头最终结束的时间。

      • 整个接水过程结束,必须等到最慢的那个龙头停下来。
      • 所以,答案是 time 数组里的最大值

      6. 数据范围提示

      • n10000n \le 10000m100m \le 100
      • 这意味着你每次找最小值的操作(遍历 mm 次)完全可以直接写循环找,不需要用堆(优先队列)等高级数据结构,暴力找最小值的总复杂度大约是 O(n×m)O(n \times m),也就是 10610^6 级别,计算机一眨眼就跑完了。

      总结代码框架:

      1. 读入 n, m
      2. 读入所有人的接水量 w[]
      3. 定义一个数组 s[] 用来存储 m 个龙头的累计时间
      4. 先把前 m 个人的时间填入 s[]
      5. 从第 m+1 个人开始循环到第 n 个人:
          a. 遍历 s[] 找到最小值的下标 min_idx
          b. s[min_idx] += 当前这个人的时间
      6. 遍历 s[] 找到最大值,输出
      

      按照这个逻辑去写,代码会非常短且清晰。加油!


      这个版本的答案有什么错

      #include <bits/stdc++.h>
      using namespace std;
      
      int main(){
          int n,m;
          cin>>n>>m;
          int t[m]={0};
          int w[n]={0};
          for(int i=0;i<n;i++){
              cin>>w[i];
          }
          //开始第一批m个人接水
          for(int j=0;j<m;j++){
              t[j]=w[j];
          }
          //循环条件:下一个接水的人去找位置接水
          for(int k=m+1;k<n;k++){
              //准备接水,要找那个水龙头是时间最小的
              int mint=INT_MAX;
              int minidx=0;
              for(int i=0;i<m;i++){
                  if(t[i]<mint){
                      minidx=i;
                      mint=w[i];
                  }
              }
              //找到最小的水龙头位置了minidx
              t[minidx]+=w[k];//去接水,更新时间
          }
          //全部人都接完了,找最大的水龙头时间
          int maxt=0;
          for(int i=0;i<m;i++){
              if(t[i]>maxt){
                  maxt=t[i];
              }
          }
          cout<<maxt<<endl;
      }
      
      

      同学,你写得很接近了!但是你的程序里有两个非常严重的逻辑错误,这会导致答案完全不对。

      我们来看看问题出在哪里:

      1. 致命错误:更新最小值时的赋值错了

      请看这段代码:

      if(t[i] < mint){
          minidx = i;
          mint = w[i]; // <--- 错误在这里!!!
      }
      

      原因分析

      • t[i] 代表第 i 号水龙头当前累计的时间。
      • mint 是用来记录“当前所有水龙头里最小的时间”。
      • 当你发现 t[i] 更小时,应该把 t[i] 的值赋给 mint
      • 你写成了 mint = w[i]。注意:i 是水龙头的编号(0到m-1),而 w 是存的接水量的数组。w[i] 只是第 i 个人的接水量,并不是这个水龙头累计的时间!

      修正

      mint = t[i]; // 应该是把这个水龙头的时间赋给 mint
      

      2. 致命错误:漏掉了第 m 个人(下标错误)

      请看循环的起始条件:

      for(int k = m + 1; k < n; k++) // <--- 错误在这里
      

      原因分析

      • 数组下标是从 0 开始的。
      • m 个人占用了 w[0]w[m-1]
      • 下一个要接水的人,下标应该是 m
      • 你写的是 k = m + 1,这意味着你直接跳过了下标为 m 的那个人(也就是第 m+1 个人),让他“人间蒸发”了。

      修正

      for(int k = m; k < n; k++) // 从第 m 个下标开始
      

      3. 潜在风险:变长数组 (VLA)

      int t[m]={0};
      int w[n]={0};
      

      在标准的 C++ 中,不能用变量来定义数组长度(虽然有些编译器支持,但在比赛中很不安全,容易爆零或编译错误)。 建议:根据题目数据范围(n10000,m100n \le 10000, m \le 100)定义固定大小的全局数组,或者大一点的局部数组。


      修改后的正确代码

      #include <bits/stdc++.h>
      using namespace std;
      
      int main(){
          int n, m;
          cin >> n >> m;
          
          // 建议开固定大小的数组,防止编译器报错
          // 题目范围 n<=10000, m<=100
          int w[10005]; 
          int t[105] = {0}; // 水龙头数组初始化为0
      
          for(int i = 0; i < n; i++){
              cin >> w[i];
          }
      
          // 1. 第一批 m 个人先把水龙头占满
          for(int j = 0; j < m; j++){
              t[j] = w[j];
          }
      
          // 2. 剩下的人(下标从 m 开始)找空位
          // 修正点1:k 从 m 开始,不是 m+1
          for(int k = m; k < n; k++){ 
              
              // 找当前哪个水龙头时间最短
              int mint = INT_MAX;
              int minidx = 0;
              
              for(int i = 0; i < m; i++){
                  if(t[i] < mint){
                      minidx = i;
                      mint = t[i]; // 修正点2:这里必须是 t[i],不是 w[i]
                  }
              }
              
              // 让这个人去最快空闲的水龙头接水
              t[minidx] += w[k];
          }
      
          // 3. 找最大的时间作为结束时间
          int maxt = 0;
          for(int i = 0; i < m; i++){
              if(t[i] > maxt){
                  maxt = t[i];
              }
          }
          cout << maxt << endl;
          return 0;
      }
      

      把这两个地方改过来,你的逻辑就完全通了!快去试试吧。

      • 0
        @ 2025-11-27 14:42:40
        #include <bits/stdc++.h>
        using namespace std;
        
        int main(){
            int n,m;
            cin>>n>>m;
            // int t[m]={0};//潜在风险:不建议用变量作为数组的长度
            // int w[n]={0};//潜在风险:不建议用变量作为数组的长度
            int t[101]={0};
            int w[10001]={0};
        …    }
            //全部人都接完了,找最大的水龙头时间
            int maxt=0;
            for(int i=0;i<m;i++){
                if(t[i]>maxt){
                    maxt=t[i];
                }
            }
            cout<<maxt<<endl;
        }
        
        • 0
          @ 2025-9-9 23:45:49

          C++ :

          #include<iostream>
          using namespace std;
          int T[200];
          int main()
          {
          	int n,m,t;
          	cin >> n >> m;
          	for(int i=0; i<n; ++i)
          	{
          		cin >> t;
          		int min_index = 0;
          		for(int j=1; j<m; ++j)
          		{
          			if(T[j] < T[min_index])
          				min_index = j;
          		}
          		T[min_index] += t;
          	}
          	int max_index = 0;
          	for(int i=1; i<m; ++i)
          	{
          		if(T[i] > T[max_index])
          			max_index = i;
          	}
          	cout << T[max_index] << endl;
          	return 0;
          }
          

          Pascal :

          var a:array[1..11000] of integer;
              b:array[1..100] of integer;
              i,j,n,m,next,sum:longint;
          function check:boolean;
          var i:integer;
          begin
            for i:=1 to m do if a[b[i]]>0 then exit(true);
            check:=false;
          end;
          begin
            readln(n,m);
            for i:=1 to n do read(a[i]);
            for i:=1 to m do b[i]:=i;
            next:=m+1;
            repeat
              for i:=1 to m do
              begin
                dec(a[b[i]]);
                if a[b[i]]=0 then
                begin
                  b[i]:=next;
                  inc(next);
                end;
              end;
              inc(sum);
            until not check;
            writeln(sum);
          end.
          
          • 1

          信息

          ID
          752
          时间
          1000ms
          内存
          128MiB
          难度
          10
          标签
          递交数
          1
          已通过
          1
          上传者