题解-运动会

题目描述

学校将要举行运动会。老师希望聪明的小C对运行项目设置优化。
学校设计了m 项运动,让 n 个同学对这m项运行的喜爱程度进行选择。
每个同学有⼀个 1~m 的排列,表⽰对m 项运动的喜爱排名(从⾼到低)。如 m = 3时,某⼈的排列顺序为3,1,2 ,表⽰他最喜欢项⽬ 3,其次为项⽬1,最后为项⽬2。
小C的任务是要选取⼀些运动,举办这些运动的⽐赛。运动员会参加被举办的运动中,⾃⼰最喜欢的那⼀个运动参加。
你需要选出⼀些运动,使得举办这些运动时,参加⼈数最多的那⼀项 运动参赛⼈数最少,输出这个⼈数。

输入

输⼊有N+1 ⾏。第⼀⾏有两个整数n,m(1<=n,m<=300),表⽰⼈数和待选 的项⽬数量。
接下来n ⾏,每⾏有 m个整数,是1~m 的⼀个排列表⽰每个⼈对 m的项⽬的喜爱排名。

输出

输出只有⼀⾏,包含⼀个整数,即最优⽅案下,参加⼈数最多的那⼀项运动的参赛⼈数。

样例

样例输入

4 5
5 1 3 4 2
2 5 3 1 4
2 3 1 4 5
2 5 4 3 1

样例输出

2

分析

我们可以用贪心逆向思维来思考这道题。

从m个项目中选出若干个,可以等价为,现在m个项目都已经被选择,需要从中删除若干个

因此,对于某个局面,我们可以贪心的删除掉选择的人数最多的项目,每次取最小值

简单证明:假设当前局面玩的人数最多的是第k个项目,

如果不删除第k个项目,那么(玩的人数最多的项目玩的人数)将始终大于等于(玩第k个项目的人数),即不可能出现更优解

如果删除第k个项目,那么(玩的人数最多的项目玩的人数)可能会低于(玩第k个项目的人数),也可能会高于(玩第k个项目的人数),即有可能出现更优解

综上所述,对于每种局面,删除掉(玩的人数最多的项目),每次取最小值,即为正确且最优的贪心策略。

所以,我们可以先假设所有的项目都被选择,然后根据贪心策略,不断删除掉选的人数最多的项目,并且取最小值。

代码

#include<bits/stdc++.h>
 
using namespace std;
 
const int N = 300 + 10;
 
int n,m;
int a[N][N];
int cnt[N]; // 统计某个项目被多少人选择
bool st[N]; // 标记某个项目是否被删除掉
int res;
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            cin >> a[i][j];
 	
    res = n;
    
    // 初始局面为所有的项目都被选择
    for(int k = 1;k < m;k++){ // 由于最少需要选择一个项目举行,所以最多删除 m-1 次
        for(int i = 1;i <= n;i++) cnt[i] = 0; // 对于每个局面,初始化
         
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= m;j++)
                if(!st[a[i][j]]){
                    // 因为对1~m的喜欢程度是递减的,所以第一个没被删除的项目即为目前所有项目中最喜欢的
                    cnt[a[i][j]]++;
                    break;
                }
 
        int tmp = 1,pos = 0; // tmp指选的人数最多的项目选的人数,pos指选的人数最多的项目的编号
        for(int i = 1;i <= m;i++)
            if(!st[i] && tmp < cnt[i]){
                tmp = cnt[i];
                pos = i;
            }
 
        res = min(res,tmp); // 每次取最小值
 
        st[pos] = true; // 将选的人数最多的项目删去
    }
 
    cout << res;
 
    return 0;
}
  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值