东大acm预备队训练第六周(贪心)

1 P1199 [NOIP2010 普及组] 三国游戏

[NOIP2010 普及组] 三国游戏

题目描述

小涵很喜欢电脑游戏,这些天他正在玩一个叫做《三国》的游戏。

在游戏中,小涵和计算机各执一方,组建各自的军队进行对战。游戏中共有 N N N
位武将( N N N为偶数且不小于 4 4 4),任意两个武将之间有一个“默契值”,表示若此两位武将作为一对组合作战时,该组合的威力有多大。游戏开始前,所有武将都是自由的(称为自由武将,一旦某个自由武将被选中作为某方军队的一员,那么他就不再是自由武将了),换句话说,所谓的自由武将不属于任何一方。

游戏开始,小涵和计算机要从自由武将中挑选武将组成自己的军队,规则如下:小涵先从自由武将中选出一个加入自己的军队,然后计算机也从自由武将中选出一个加入计算机方的军队。接下来一直按照“小涵→计算机→小涵→……”的顺序选择武将,直到所有的武将被双方均分完。然后,程序自动从双方军队中各挑出一对默契值最高的武将组合代表自己的军队进行二对二比武,拥有更高默契值的一对武将组合获胜,表示两军交战,拥有获胜武将组合的一方获胜。

已知计算机一方选择武将的原则是尽量破坏对手下一步将形成的最强组合,它采取的具体策略如下:任何时刻,轮到计算机挑选时,它会尝试将对手军队中的每个武将与当前每个自由武将进行一一配对,找出所有配对中默契值最高的那对武将组合,并将该组合中的自由武将选入自己的军队。
下面举例说明计算机的选将策略,例如,游戏中一共有 6 6 6个武将,他们相互之间的默契值如下表所示:

双方选将过程如下所示:

小涵想知道,如果计算机在一局游戏中始终坚持上面这个策略,那么自己有没有可能必胜?如果有,在所有可能的胜利结局中,自己那对用于比武的武将组合的默契值最大是多少?

假设整个游戏过程中,对战双方任何时候均能看到自由武将队中的武将和对方军队的武将。为了简化问题,保证对于不同的武将组合,其默契值均不相同。

输入格式

共 N 行。

第一行为一个偶数 N N N,表示武将的个数。

2 2 2行到第 N N N行里,第 i + 1 i+1 i+1行有 N i N_i Ni个非负整数,每两个数之间用一个空格隔开,表示 i i i号武将和 i + 1 , i + 2 , … , N i+1,i+2,…,N i+1,i+2,,N号武将之间的默契值( 0 ≤ 0≤ 0默契值 ≤ 1 , 000 , 000 , 000 ≤1,000,000,000 1,000,000,000)。

输出格式

1 1 1 2 2 2行。

若对于给定的游戏输入,存在可以让小涵获胜的选将顺序,则输出 1 1 1,并另起一行输出所有获胜的情况中,小涵最终选出的武将组合的最大默契值。如果不存在可以让小涵获胜的选将顺序,则输出 0 0 0

样例 #1
样例输入 #1

6
5 28 16 29 27
23 3 20 1
8 32 26
33 11
12

样例输出 #1

1
32

样例 #2
样例输入 #2

8
42 24 10 29 27 12 58
31 8 16 26 80 6
25 3 36 11 5
33 20 17 13
15 77 9
4 50
19

样例输出 #2

1
77

提示

【数据范围】

对于 40 % 40\% 40%的数据有 N ≤ 10 N≤10 N10

对于 70 % 70\% 70%的数据有 N ≤ 18 N≤18 N18

对于 100 % 100\% 100%的数据有 N ≤ 500 N≤500 N500

题目解析

对于此题我们要充分理解题意,制定最好的策略。
我们要明白因为机器人的策略,使得我们永远不可能取得最大默契值的一对武将,我们只能取得第二大默契值的武将。所以取武将时我们都是尽可能的去取第二大默契值的武将。
这时会有两种情况出现。
第一种:交叉
假设33和32,分别为所有默契值第一大的和第二大的。
武将4和武将5中我们选择武将5,因为武将5的第二大默契值更大,这之后机器人选择武将4,然后我们选择武将3。
这时我们取到了能取到的最大值,机器人取得的默契值不可能比我们大,所以我们必胜。
在这里插入图片描述
第二种:独立
依旧假设33和32,分别为所有默契值第一大的和第二大的。
31和30分别为所有默契值第三大和第四大的。
这时我们在武将4、武将5和武将1、武将2这两个组合中,就难以选择,因为无论选择哪个组合都拿不到其组合的最大值。
在这里插入图片描述

这时独立的后续依旧可以延伸出两种情况:
(1)独立的情况可以延伸到交叉出现
看看哪个能延伸出交叉情况,哪个交叉出的默契值更大,找到第二大默契值。
例如这里武将1、2组合延伸出的31很明显比武将4、5组合延伸出的30大。
我们选择武将2,机器人选择武将1,破坏了32这个默契值,我们再选择武将5,机器人选择武将4,破坏了33这个值。
我们再选择武将3,与武将2达到能选到的最大值31,我们赢。
在这里插入图片描述
(2)独立延伸的结果依旧是独立,直到所有武将选完依旧是独立
这种情况下依旧是人类获胜,因为人类有优先选择权,机器人总是跟着人类选择,我们可以将所有独立情况的武将串联起来,依旧可以选到每个武将默契值第二大的。
总结:人类必胜,每个武将和其他武将的第一大默契值,总是被机器人破坏,而我们取到的最大默契值,只能是每个武将和其他武将的第二大默契值,并且我们总能取到最大的那个,所以人类必胜,我们只需找到每个武将和其他武将的第二大默契值中最大的那个即为答案。

代码

#include <bits/stdc++.h>
using namespace std;

int N,ans,mp[501][501];

int main()
{
    cin >> N;
    for (int i = 1; i <= N - 1; ++i)
    {
        for (int j = i + 1; j <= N; ++j)
        {
            scanf("%d", &mp[i][j]);
            mp[j][i]=mp[i][j];
        }
    }
    for (int i = 1; i <= N; ++i)
    {
        sort(mp[i]+1,mp[i]+N+1,greater<int>());
        ans=max(ans,mp[i][2]);
    } 
    cout << "1" << endl
         << ans << endl;
    return 0;
}

2 P1007 独木桥

独木桥

题目背景

战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳
1 1 1 个人通过。假如有 2 2 2 个人相向而行在桥上相遇,那么他们 2 2 2 个人将无法绕过对方,只能有 1 1 1
个人回头下桥,让另一个人先通过。但是,可以有多个人同时呆在同一个位置。

题目描述

突然,你收到从指挥部发来的信息,敌军的轰炸机正朝着你所在的独木桥飞来!为了安全,你的部队必须撤下独木桥。独木桥的长度为
L L L,士兵们只能呆在坐标为整数的地方。所有士兵的速度都为 1 1 1,但一个士兵某一时刻来到了坐标为 0 0 0 L + 1 L+1 L+1
的位置,他就离开了独木桥。

每个士兵都有一个初始面对的方向,他们会以匀速朝着这个方向行走,中途不会自己改变方向。但是,如果两个士兵面对面相遇,他们无法彼此通过对方,于是就分别转身,继续行走。转身不需要任何的时间。

由于先前的愤怒,你已不能控制你的士兵。甚至,你连每个士兵初始面对的方向都不知道。因此,你想要知道你的部队最少需要多少时间就可能全部撤离独木桥。另外,总部也在安排阻拦敌人的进攻,因此你还需要知道你的部队最多需要多少时间才能全部撤离独木桥。

输入格式

第一行共一个整数 L L L,表示独木桥的长度。桥上的坐标为 1 , 2 , ⋯   , L 1, 2, \cdots, L 1,2,,L

第二行共一个整数 N N N,表示初始时留在桥上的士兵数目。

第三行共有 N N N 个整数,分别表示每个士兵的初始坐标。

输出格式

共一行,输出 2 2 2 个整数,分别表示部队撤离独木桥的最小时间和最大时间。 2 2 2 个整数由一个空格符分开。

样例 #1
样例输入 #1

4
2
1 3

样例输出 #1

2 4

提示

对于 100 % 100\% 100% 的数据,满足初始时,没有两个士兵同在一个坐标, 1 ≤ L ≤ 5 × 1 0 3 1\le L\le5\times 10^3 1L5×103 0 ≤ N ≤ 5 × 1 0 3 0\le N\le5\times10^3 0N5×103,且数据保证 N ≤ L N\le L NL

题目解析

当两个士兵面对面相向行来,两个人同时转身,我们也可以视作两个士兵交换位置或者两者穿过对方继续前行,这样题意解决就十分简单。

代码

#include <bits/stdc++.h>
using namespace std;

int N,L,pos,ans_min,ans_max;

int main()
{
    scanf("%d %d",&L,&N);
    while(N--)
    {
        scanf("%d",&pos);
        ans_max=max(ans_max,max(L+1-pos,pos));
        //找到每个士兵的最远行程的最大一个
        ans_min=max(ans_min,min(L+1-pos,pos));
        //找到每个士兵的最近行程的最大一个
    }
    cout<<ans_min<<" "<<ans_max<<endl;
    return 0;
}

3 P1223 排队接水

排队接水

题目描述

n n n 个人在一个水龙头前排队接水,假如每个人接水的时间为 T i T_i Ti,请编程找出这 n n n 个人排队的一种顺序,使得 n n n
个人的平均等待时间最小。

输入格式

第一行为一个整数 n n n

第二行 n n n 个整数,第 i i i 个整数 T i T_i Ti 表示第 i i i 个人的等待时间 T i T_i Ti

输出格式

输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

样例 #1
样例输入 #1

10
56 12 1 99 1000 234 33 55 99 812

样例输出 #1

3 2 7 8 1 4 9 6 10 5
291.90

提示

n ≤ 1000 , t i ≤ 1 0 6 n \leq 1000,t_i \leq 10^6 n1000,ti106,不保证 t i t_i ti 不重复。

t i t_i ti 重复时,按照输入顺序即可(sort 是可以的)

题目解析

优先让接水时间短的人接水。

代码

#include <bits/stdc++.h>
using namespace std;

int N,w;
double ans;
pair<int,int> a[1001];

int main()
{
    cin>>N;
    for(int i=1;i<=N;++i)
    {
        scanf("%d",&w);
        a[i]=make_pair(i,w);
    }
    sort(a+1,a+N+1,[](pair<int,int> x,pair<int,int> y){
        return x.second<y.second;
    });
    for(int i=1;i<=N;++i)
    {
        cout<<a[i].first<<" ";
        ans+=(N-i)*a[i].second;
    }
    cout<<endl;
    ans/=N;
    printf("%.2f",ans);
	return 0;
}

4 P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n − 1 n-1 n1 次合并之后,
就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 1 1 ,并且已知果子的种类
数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 3 3 3 种果子,数目依次为 1 1 1 2 2 2 9 9 9 。可以先将 1 1 1 2 2 2 堆合并,新堆数目为 3 3 3 ,耗费体力为
3 3 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 12 12 ,耗费体力为 12 12 12 。所以多多总共耗费体力
= 3 + 12 = 15 =3+12=15 =3+12=15 。可以证明 15 15 15 为最小的体力耗费值。

输入格式

共两行。 第一行是一个整数 n ( 1 ≤ n ≤ 10000 ) n(1\leq n\leq 10000) n(1n10000) ,表示果子的种类数。

第二行包含 n n n 个整数,用空格分隔,第 i i i 个整数 a i ( 1 ≤ a i ≤ 20000 ) a_i(1\leq a_i\leq 20000) ai(1ai20000) 是第 i i i
种果子的数目。

输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2 31 2^{31} 231

样例 #1
样例输入 #1

3
1 2 9

样例输出 #1

15

提示

对于 30 % 30\% 30% 的数据,保证有 n ≤ 1000 n \le 1000 n1000

对于 50 % 50\% 50% 的数据,保证有 n ≤ 5000 n \le 5000 n5000

对于全部的数据,保证有 n ≤ 10000 n \le 10000 n10000

题目解析

优先选择质量最小的两堆进行合并,合并后成为新的一堆。

代码

#include <bits/stdc++.h>
using namespace std;

int N,x,ans;
priority_queue<int,vector<int>,greater<int> > Q;

int main()
{
    cin>>N;
    for(int i=1;i<=N;++i)
    {
        scanf("%d",&x);
        Q.push(x);
    }
    while(N>1)
    {
        int num=Q.top();
        Q.pop();
        num+=Q.top();
        Q.pop();
        ans+=num;
        Q.push(num);
        N--;
    }
    cout<<ans;
    
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷神星ceres

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值