[20191004机房测试] C++锦标赛

有一个比赛已经有 n 个人参加,并且互相之间已经进行了比赛
每一个人都有一个得分用于最后排名
你作为第 n + 1 个参赛者,需要与之前的每一个人打一场比赛,初始得分为 0
对于每场比赛,有两个人参加,不会存在平局,胜者得分增加 1,败者得分不变
最后按照得分从高到低来排名,假设有人与你最终得分相同
那么如果那个人曾经输给了你就会排在你后面,否则会排在你的前面
如果你赢了某个人,就需要消耗相应的 RP 值
现在你可以决定赢那些人,求最少需要消耗多少 RP 值才能到前k名

50分做法:还是枚举二进制串……居然有50分

100分做法:
这个满分做法是真的玄学,我们需要分三种情况讨论来贪心求解:

  1. 如果一个人的胜场和自己相同或者刚好少1,不打就会被挤下去,打了就可以升一位
  2. 如果一个人的胜场比自己多,打不打都没什么差异……
  3. 如果一个人的胜场比自己少2或者更多,那就可以不用打了
    然后贪心就好

代码:

#include<bits/stdc++.h>
#define ll long long
#define maxn 2505
using namespace std;

int T,n,k,gol,num;
ll ans;
bool used[maxn];

struct People
{
    int p, e;
}p[maxn];

bool cmp1(People x, People y) {return x.p > y.p;}
bool cmp2(People x, People y) {return x.e < y.e;}

bool check()
{
    int res=0;
    for(register int i=1;i<=n;++i)
        if(p[i].p>n) res++;
    return res>=k;
}

ll work1()
{
    ll ret=0;
    int cnt=gol,num1=0,num2=num;
    for(register int i=1;i<=n;++i)
    {
        used[i]=0;
        if(p[i].p==gol-1) ++num1;
    }
    for(register int i=1;i<=n&&num2;++i)
    {
        if(p[i].p==gol)
        {
            ret=ret+p[i].e;
            used[i]=1;
            --cnt;
            --num2;
        }
    }
    for(register int i=1;i<=n&&num1;++i)
    {
        if(!used[i]&&(p[i].p==gol||p[i].p==gol-1))
        {
            ret=ret+p[i].e;
            used[i]=1;
            --cnt;
            --num1;
        }
    }
    for(register int i=1;i<=n&&cnt;++i)
    {
        if(!used[i])
        {
            ret=ret+p[i].e;
            --cnt;
        }
    }
    return ret;
}

ll work2()
{
    ll ret=0;
    int num2=num,cnt=gol+1;
    for(register int i=1;i<=n;++i) used[i]=0;
    for(register int i=1;i<=n&&num2;++i)
    {
        if(p[i].p==gol||p[i].p==gol+1)
        {
            used[i]=1;
            ret=ret+p[i].e;
            --cnt;
            --num2;
        }
    }
    for(register int i=1;i<=n&&cnt;++i)
    {
        if(!used[i])
        {
            ret=ret+p[i].e;
            --cnt;
        }
    }
    return ret;
}

ll work3()
{
    ll ret=0;
    for(register int i=1;i<=gol+2;++i)
        ret=ret+p[i].e;
    return ret;
}

template<class T>inline void read(T &res)
{
    char c;T flag=1;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

int main()
{
    freopen("tournament.in","r",stdin);
    freopen("tournament.out","w",stdout);
    read(T);
    while(T--)
    {
        read(n);read(k);
        for(register int i=1;i<=n;++i) scanf("%d%d",&p[i].p,&p[i].e);
        if(k==n+1)
        {
            printf("0\n");
            continue;
        }
        if(check())
        {
            printf("-1\n");
            continue;
        }
        sort(p+1,p+n+1,cmp1);
        gol=p[k].p;
        num=1;
        while(p[k+num].p==p[k].p&&k+num<=n) ++num;
        sort(p+1,p+n+1,cmp2);
        ans=min(work1(),min(work2(),work3()));
        printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/tqr06/p/11622889.html

时间: 2024-10-10 14:36:25

[20191004机房测试] C++锦标赛的相关文章

[20191004机房测试] ZGY的早餐

ZGY 每天早上要从宿舍走路到机房,顺便从学校小卖部购买早饭,当然机智的 ZGY 一定会走最短路 学校的路可以看成一无向联通张图,图上有 n 个点,m 条边,每一个点都有一个唯一的编号 1~n 每一条边有一个边权,表示两个点之间的距离,ZGY 的宿舍在 S 点,机房在 T点,而小卖部在 H 点 现在 ZGY 想知道从宿舍经过小卖部到达机房的最短距离 不过因为在这个世界上有 Q个 ZGY,所以你必须回答 Q 个问题 很棒的数据分治题 读入里面说了会读入测试点编号-- 其实是很明显的暗示了-- 一半

[20191004机房测试] 三角

有一个 n 层的数字三角形 每次可以从第 i 层的第 j 个走到第 i + 1 层的第 j 个或是第 j +1 个,直到走到第 n 层 从第 1 层走到第 n 层的一种方案成为一条路径,路径的权值为路径上点权值之和 求权值前 k 大的路径(存在多个正确答案) 20分做法: 枚举二进制串,暴力枚举所有走法 复杂度:\(\Theta(2^n)\) 60分做法: 对每个点开一个堆,维护从下往上的前k大值,由于只和下一层有关,可以滚动 复杂度:\(\Theta(n^3\log{(n)})\) 100分做

机房测试3:C++锦标赛(贪心)

题目: 分析: 首先理解题意:zyg要和每一个人都打比赛,且只有输和赢两种情况,也就是说没打赢的人最后得分要++. 我们希望zyg打赢的人尽量地少,且rp值小. 先对比分大小排序,估计一下对应排名的最小分数sc,再按rp从小到大排序,然后分情况贪心: 1.使其最终得分为sc+2: 只需要打赢前sc+2个人即可,没有其他人会影响到他. 2.最终得分为sc+1: 我们担心得分为sc的人因为没有被打赢,得分+1而排在zyg前面,所以应该先处理得分为sc的人,把他们都打赢. (同时也要打赢sc+1的人,

Host1Plus主机商8个机房测试IP体验

Host1Plus商家也算是比较老的海外主机商(英国),当初进入中国市场也是比较早的,记得那时候支持支付宝的海外主机商尤其的深受用户喜欢.因为我 们大部分网友.站长最多有一个支付宝,很少有双币信用卡或者贝宝支付美元的能力.不过后来几年,HOST1PLUS商家逐渐的落寞,主要原因在于其他商家 的出现,以及他们本身营销能力和产品的策略. 尤其是提供的虚拟主机和VPS主机,价格和方案不是太符合中国用户的口味,配置比较低,而且机房较 少.不过商家应该意识到这一点,在最近2年改动挺大的,今天公司业务正好有

机房测试9.22

题解之前 中秋只有一天假! build 以为并查集水题,发现有历史版本,于是可持久化并查集一波(爆0了) 其实有简单点的做法:用二元组记录每一次变化的siz和fa,二分查询即可. 我还在肝可持久并查集,所以就没有代码了. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; co

机房测试9.23

题解之前 今天还行啊. Set 肯定要取模. 开始还在想vector这种操作,后来一个dalao发现一定有解,然后有发现一定有一种答案是连续的一段区间,于是就切掉了. 看了题解才发现我们只是运气好. 前缀和如果有n取值,就选%n=0的那一个,不然至多只剩n-1个取值,然而又有n个前缀和. 所以必然有两个相等,输出这之间的下标即可. #include<cstdio> #include<cctype> #include<cstring> #define FN "s

机房测试10.6

砖块 很简单的水题,写起来也很简单. 模拟写的很短,是否也是码力的体现? #include<cstdio> #include<cstring> #include<map> #define FN "block" int mp[1105][1105],dao,ans; char s[105]; int x,y,dx[4]={0,0,-1,1},dy[4]={1,-1,0,0}; std::map<char,int> m; void init(

[机房测试]数字谜题

小 X 同学有很强的计算能力,现在他正在玩一个游戏. 现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的个数 小 X 不断的进行操作,直到这个数变成 1 为止 由于小 X 的计算能力很强,他现在给出一 n 他想知道有多少不超过 n 的正整数会在 k 次操作后变成 1 由于答案可能很大,请对 1000000007 取模 因为数据范围是\(2^{1000}\),所以一次操作后最多就只有1000个1了 因此直接暴力处理出1~1000所有数变为1的操作次数,把次数为 \(k-1\

机房测试1:string(线段树)

题目: 分析: 暴力:每一次对区间暴力排序. 优化:如果可以知道一个区间中有哪种字符,这些字符分别有多少个,就可以直接按字典序枚举,将它们快速地插入区间中了. 题中有一个重要信息:只有小写字母,即只有26种字符. 第一种方法: 可以用一个线段树来维护,每个节点储存26个字符在这个区间中的对应情况.每次修改,就query统计出所求区间每一种字符的个数. 然后再区间修改,具体见代码. #include<bits/stdc++.h> using namespace std; #define mid