【贪心算法】

[NOIP2010 普及组] 三国游戏

题目描述

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

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

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

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

双方选将过程如下所示:

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

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

输入格式

共 N 行。

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

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

输出格式

1 1 1 或 $2 $行。

若对于给定的游戏输入,存在可以让小涵获胜的选将顺序,则输出$ 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%$的数据有 N ≤ 10 N≤10 N10

对于$ 70% 的 数 据 有 的数据有 N≤18$。

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

题目很长,看似很复杂,但是经过一番冷静分析后,发现题目并不难。

初步分析。

1.计算机拿走的武将都与小涵所拿走的武将的默契值最大。
  所以小涵永远拿不到与自己所选武将默契值最高的武将。
2.既然两个武将的默契值相等,那么f(A,B)=f(B,A)。

接下来,我们继续分析

假设有 A1,A2,A3,A4 共4个武将。且f(A,B)>f(A,C)>f(A,D)>f(B,D)
我们来做一个假设:
如果小涵选择了A,则计算机必定选择B。那么小涵无法得到最优解,该怎么办呢?
没错,我们可以选择与A默契值第二大的C,那么计算机只能选择D,所以小涵必赢。

一番冷静地分析后,长长的题目就被简化了。

简化后的题目:给你一堆数,请找出其中第二大的数。

那么,代码便完成了。

打上代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N=505;
int a[N][N];
int ans;
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <=n; i++)
    {
        for(int j=i+1;j<=n;j++){
            cin>>a[i][j];
            a[j][i]=a[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        sort(a[i]+1,a[i]+n+1);
        ans=max(ans,a[i][n-1]);//取次大中最大的数
    }
    cout << 1 << endl;
    cout << ans;
}

独木桥

题目背景

战争已经进入到紧要时间。你是运输小队长,正在率领运输部队向前线运送物资。运输任务像做题一样的无聊。你希望找些刺激,于是命令你的士兵们到前方的一座独木桥上欣赏风景,而你留在桥下欣赏士兵们。士兵们十分愤怒,因为这座独木桥十分狭窄,只能容纳 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

两个士兵撞在一起时,从你的视角看会发生什么?当然他们认为他们都掉头了,但因为你在特高的地方,你会认为他们“穿过”了对方。换言之,这与他们相互穿过并没有任何区别。

然后我们就可以把士兵分开了。比方说有一个士兵在位置3,开始时向右,那么一定有一个士兵在两秒后在位置5。虽然这两个家伙可能不是同一个人,但由于士兵都是相同的,我们可以认为他们相同。

那么我们就可以把所有士兵分开。首先,我们把他们一个个读进去。然后,对于每一个士兵,他有向左和向右两种选择。设士兵在位置p,如果向左,需要p时间单位;向右,需要L-p+1个。分别取max和min

最大值:最靠近端点两个人各自向对方走,时间较长的那个人的时间

最小值:所有人中走完桥最小值中的最大值

#include<iostream>
using namespace std;
const int N=5e4;
int a[N];
int ansmin;
int ans1;
int ans2;
int ansmax;
int main(){
    int L;
    cin>>L;
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        ansmin=min(L+1-a[i],a[i]);
        ans1=max(ans1,ansmin);//最短时间
        ansmax=max(L+1-a[i],a[i]);
        ans2=max(ans2,ansmax);//最长时间
    }
    cout<<ans1<<" "<<ans2;
}

排队接水

题目描述

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 是可以的)

这题和暑假服务等待时间那道题几乎是一模一样QAQ

思路很简单,如果不从实现代码的角度,初中生就能想出来

先排时间短的,其他人等待的时间总和就更短

#include <algorithm>
#include <iostream>
using namespace std;
const int N = 5e6;
struct pe
{
    int id;
    int t;
} t[N];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> t[i].t;
        t[i].id = i;//利用结构体将编号和等待时间联系起来
    }
    sort(t + 1, t + 1 + n, [](pe a, pe b)//重载快排
         { return (a.t < b.t); });
    for (int i = 1; i <= n; i++)
    {
        cout << t[i].id << " ";//依次输出编号
    }
    double sum = 0;
    for (int i = 1; i <= n; i++)
    {

        sum += t[i].t * (n - i);//先排时间短的,其他人等待的时间总和就更短
    }
    cout << endl;
    printf("%.2f", sum * 1.0 / n);
}

[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

这题和暑假那道合并石子的题也是一模一样的QAQ

这题如果用优先队列就非常简单了

#include <iostream>
#include <queue>
using namespace std;
int ans;
priority_queue<int, vector<int>, greater<int>> que;//
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int guozi;
        cin >> guozi;
        que.push(guozi);//优先队列,将输入的果子按由大到小顺序放入队列中
    }
    int a, b;
    while (que.size() > 1)
    {
        a = que.top();
        que.pop();
        b = que.top();//取队列中最小的两个数
        ans += a + b;//将这两个数加给耗费的总力气
        que.pop();
        que.push(a + b);//两堆果子合成一堆再入队
    }
    cout << ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值