寒假笔记·神奇的模拟

等效化

例题:P1007 独木桥

原题地址
题目描述

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

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

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

输入输出格式

输入格式:
第一行:一个整数L,表示独木桥的长度。桥上的坐标为1…L
第二行:一个整数N,表示初始时留在桥上的士兵数目

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

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

输入输出样例

输入样例#1:
2
1 3
输出样例#1:
2 4
说明

初始时,没有两个士兵同在一个坐标。

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

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

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

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
    int n,l,p,maxv=0,minv=0;
    scanf("%d%d",&l,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&p);
        maxv=max(maxv,max(l-p+1,p));
        minv=max(minv,min(l-p+1,p));
    }
    printf("%d %d",minv,maxv);
    return 0;
}

一边倒

例题:P1031 均分纸牌

原题地址
题目描述

有N堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为1堆上取的纸牌,只能移到编号为2的堆上;在编号为N的堆上取的纸牌,只能移到编号为N-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如N=4,4堆纸牌数分别为:

①9②8③17④6
移动33次可达到目的:

从 ③ 取4张牌放到 ④ (9,8,13,10)-> 从 ③ 取33张牌放到 ②(9,11,10,10)-> 从 ② 取11张牌放到①(10,10,10,10)。

输入输出格式

输入格式:
两行

第一行为:N(N堆纸牌,1≤N≤100)

第二行为:A1,A2, … ,An
输出格式:
一行:即所有堆均达到相等时的最少移动次数。

输入输出样例

输入样例#1:
4
9 8 17 6
输出样例#1:
3
代码:
这题我的方法是:

1.算平均数。

2.求每堆纸牌与平均数的关系(多1记为1,少1记为-1)。

3.当q[y](第y堆纸牌与平均数的关系)不等于0时,q[y+1]=q[y+1]+q[y],移动次数加1。

#include <iostream>  
using namespace std;  
int main()  
{ 
int a,p=0,js=0; cin >>a;int q[a];  
for (int y=0;y<a;y++){cin >>q[y]; p+=q[y];} p/=a;  
for (int y=0;y<a;y++)q[y]-=p;  
for (int y=0;y<a;y++) {if (q[y]==0)continue; q[y+1]+=q[y]; js++; }  
cout <<js;  
return 0;
}  

例题:P2970 [USACO09DEC]自私的放牧Selfish Grazing

原题地址
题目描述

Each of Farmer John’s N (1 <= N <= 50,000) cows likes to graze in a certain part of the pasture, which can be thought of as a large one-dimeensional number line. Cow i’s favorite grazing range starts at location S_i and ends at location E_i (1 <= S_i < E_i; S_i < E_i <= 100,000,000).

Most folks know the cows are quite selfish; no cow wants to share any of its grazing area with another. Thus, two cows i and j can only graze at the same time if either S_i >= E_j or E_i <= S_j. FJ would like to know the maximum number of cows that can graze at the same time for a given set of cows and their preferences.

Consider a set of 5 cows with ranges shown below:

… 1 2 3 4 5 6 7 8 9 10 11 12 13 …
… |----|----|----|----|----|----|----|----|----|----|----|----|----
Cow 1: <=:=> : : :
Cow 2: <:::=>:
Cow 3: : <
> : : :
Cow 4: : : <====:=> :
Cow 5: : : <
> : :
These ranges represent (2, 4), (1, 12), (4, 5), (7, 10), and (7, 8), respectively.

For a solution, the first, third, and fourth (or fifth) cows can all graze at the same time. If the second cow grazed, no other cows could graze. Also, the fourth and fifth cows cannot graze together, so it is impossible for four or more cows to graze.
输入输出格式

输入格式:

  • Line 1: A single integer: N

  • Lines 2…N+1: Line i+1 contains the two space-separated integers: S_i and E_i

输出格式:

  • Line 1: A single integer representing the maximum number of cows that can graze at once.

输入输出样例

输入样例#1:
5
2 4
1 12
4 5
7 10
7 8
输出样例#1:
3

代码:
这是一道比较经典的问题,注意题目所问的是奶牛的个数,如此,就应该联想到贪心的算法。(题外话:如果要求算奶牛的草场总长则应该用dp)

那么应该怎么贪心呢?

一个奶牛的需求是一个区间,左端点l与有端点r,但是对其他奶牛有影响的是右端点。

那么就对右端点进行排序,从1开始枚举,如果奶牛要求区间的左端点满足大于等于最后一次有行动的决策(即奶牛“占领”草场)的右端点,那么加入,且更新占领的状态。 代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int n,r,ans=0;
struct NA
{
    int l,r;
}e[N];
bool cmp(NA i,NA j)
{
    return i.r<j.r;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&e[i].l,&e[i].r);
    sort(e+1,e+n+1,cmp);
    for(int i=1;i<=n;i++)
        if(e[i].l>=r)
        {
            ans++;
            r=e[i].r;
        }
    printf("%d\n",ans);
    return 0;
}

大胆找规律

例题:P2755 洗牌问题

原题地址
题目描述

给你2N张牌,编号为1,2,3…n,n+1,…2n。这也是最初的牌的顺序。 一次洗牌是把序列变为n+1,1,n+2,2,n+3,3,n+4,4…2n,n。可以证 明,对于任意自然数N,都可以在经过M次洗牌后第一次重新得到 初始的顺序。编程对于小于1e8的自然数N,求出M的值。

输入输出格式

输入格式:
输入:N

输出格式:
输出:M

输入输出样例

输入样例#1:
20
输出样例#1:
20
代码:
只要1的下标k回到了原点,那么其他元素也回到了原点,并且:1的下标k如果大于n,那么1的下标k就要从k变为2*(k-n)-1;如果1的下标k小于等于n,那么1的下标k就要从k变为2*k。

#include<cstdio>
using namespace std;
int main()
{
    int n,m,t;
    scanf("%d",&n);
    t=1;
    for(m=0;;m++)
    {
        if(t>n) t=(t-n)*2-1;
        else t*=2;
        if(t==1) break;
    }
    printf("%d\n",m+1);
    return 0;
}

例题:P1334 瑞瑞的木板

原题地址
题目描述

瑞瑞想要亲自修复在他的一个小牧场周围的围栏。他测量栅栏并发现他需要N(1≤N≤20,000)根木板,每根的长度为整数Li(1≤Li≤50,000)。于是,他神奇地买了一根足够长的木板,长度为所需的N根木板的长度的总和,他决定将这根木板切成所需的N根木板。(瑞瑞在切割木板时不会产生木屑,不需考虑切割时损耗的长度)瑞瑞切割木板时使用的是一种特殊的方式,这种方式在将一根长度为x的模板切为两根时,需要消耗x个单位的能量。瑞瑞拥有无尽的能量,但现在提倡节约能量,所以作为榜样,他决定尽可能节约能量。显然,总共需要切割N-1次,问题是,每次应该怎么切呢?请编程计算最少需要消耗的能量总和。

输入输出格式

输入格式:
第一行: 整数N,表示所需木板的数量

第2到N+1行: 每行为一个整数,表示一块木板的长度

输出格式
一个整数,表示最少需要消耗的能量总和

输入输出样例

输入样例#1:
3
8
5
8
输出样例#1:
34
说明

将长度为21的木板,第一次切割为长度为8和长度为13的,消耗21个单位的能量,第二次将长度为13的木板切割为长度为5和8的,消耗13个单位的能量,共消耗34个单位的能量,是消耗能量最小的方案。
代码:
此题跟P1090差不多。P1090是合并,此题是分解,但是代码却一模一样。代码前面已经有很多dalao写了,我就来分析一下为什么这两个题的代码一模一样吧

假如你在P1090中用了一个数组P,在P1334中用了一个数组Q,设木板长(果子重量)的和是R,那么Q数组储存着你的目标木板,当这里面只有一个数的时候,这就意味着你的目标只有一个——拥有一块长为R的木板,而题目中表示你一开始就有一块肠长为R的木板了,所以那个时候你就完成任务了。合并两块目标木板则意味着你的目标是拥有一块长度为这两块木板长之和的木板(因为你可以分解这块木板以获得这两块目标木板)。因为两题中能量的消耗都是消耗两块木板(两堆果子)的和,所以答案就一样了

所以,虽然这两题代码一样,但是数组的意义是完全不一样的。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
priority_queue<long long int,vector<long long int>,greater<long long int> >qq;
int main()
{
    long long int n,i,ans=0,t,c,d;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&t);
        qq.push(t);
    }
    for(i=1;i<=n-1;i++)
    {
        c=qq.top();
        qq.pop();
        d=qq.top();
        qq.pop();
        ans=ans+c+d;
        qq.push(c+d);
    }
    printf("%lld\n",ans);
    return 0;
}

例题:P1199 三国游戏

原题地址

题目描述

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

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

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

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

在这里插入图片描述

双方选将过程如下所示:

在这里插入图片描述

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

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

输入输出格式

输入格式:
共 N 行。

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

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

输出格式:
共 1 或 2 行。

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

输入输出样例

输入样例#1:
6
5 28 16 29 27
23 3 20 1
8 32 26
33 11
12
输出样例#1:
1
32

输入样例#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。

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

对于 100%的数据有 N≤500

代码:

对于一个小涵拿的i,小涵永远拿不到和i默契最大的j,因为选了i之后计算机一定会选掉j,既然拿不到最大的,小涵可以拿次大的,同理,对于计算机拿到的一个i,计算机也拿不到与i默契最大的j,因为计算机一定是在小涵拿了j之后拿的i,所以计算的最好的牌也只是次大,这样,小涵只需要拿所有次大中最大的就能保证赢。

#include<iostream>
#include<algorithm>
#define f(i,l,r) for(i=l;i<=r;i++)
using namespace std;
const int MAXN=505;
int a[MAXN][MAXN],n;
int main()
{
    ios::sync_with_stdio(false);
    int i,j,max1,max2,ans=0;
    cin>>n;
    f(i,1,n){
        max1=max2=0;
        f(j,i+1,n){
            cin>>a[i][j];
            a[j][i]=a[i][j];
        }
        f(j,1,n){
            if(a[i][j]>max1){
                max2=max1;
                max1=a[i][j];
            }
            else if(a[i][j]>max2){
                max2=a[i][j];
            }
        }
        ans=max(ans,max2);
    }
    cout<<1<<endl<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值