USACO银组12月月赛题解

USACO银组12月月赛题解

Convention

题面

一场别开生面的牛吃草大会就要在Farmer John的农场举办了!
世界各地的奶牛将会到达当地的机场,前来参会并且吃草。具体地说,有N头奶牛到达了机场(1≤N≤105),其中奶牛i在时间ti(0≤ti≤109)到达。Farmer John安排了M(1≤M≤105)辆大巴来机场接这些奶牛。每辆大巴可以乘坐C头奶牛(1≤C≤N)。Farmer John正在机场等待奶牛们到来,并且准备安排到达的奶牛们乘坐大巴。当最后一头乘坐某辆大巴的奶牛到达的时候,这辆大巴就可以发车了。Farmer John想要做一个优秀的主办者,所以并不想让奶牛们在机场等待过长的时间。如果Farmer John合理地协调这些大巴,等待时间最长的奶牛等待的时间的最小值是多少?一头奶牛的等待时间等于她的到达时间与她乘坐的大巴的发车时间之差。
输入保证MC≥N。

输入格式

(文件名:convention.in):
输入的第一行包含三个空格分隔的整数N,M,和C。第二行包含N
个空格分隔的整数,表示每头奶牛到达的时间。

输出格式

(文件名:convention.out):
输出一行,包含所有到达的奶牛中的最大等待时间的最小值。

输入样例:

6 3 2
1 1 10 14 4 3

输出样例:

4
如果两头时间1到达的奶牛乘坐一辆巴士,时间2和时间4到达的奶牛乘坐乘坐第二辆,时间10和时间14到达的奶牛乘坐第三辆,那么等待时间最长的奶牛等待了4个单位时间(时间10到达的奶牛从时间10等到了时间14)。

题解

  • 看到题面以后以为是摆渡车...
  • 不过根据输出格式所说的最大的最小很容易想到二分答案。
    题目中求得是等待的时间,因此我们就对等待的时间进行二分答案即可。
  • 如何写二分答案的判断函数呢??
    线性扫描区间:
    如果当前奶牛之前没有坐车,就坐一辆车,等接下来的车
    如果当前奶牛的时间与第一头上车的奶牛时间差在mid以内,且容量够,上车
    否则另开一辆车
    最后得到所需车的数量
  • 判定:车的数量小于m则合法
  • 时间复杂度\(O(nlogn)\)
#include<bits/stdc++.h>
using namespace std;
const int maxn=100000;
int n,m,c;
int t[maxn]={};
inline bool check(int x)
{
    int sum=0,cnt=0,last=0;
    for (int i=1;i<=n;++i)
    {
        if (t[i]-last<=x && i!=1 && sum<c)
            sum++;//如果能乘车
        else
            last=t[i],sum=1,cnt++;//换新车
    }
    return cnt<=m;
}
int main()
{
    freopen("convention.in","r",stdin);
    freopen("convention.out","w",stdout);
    scanf("%d%d%d",&n,&m,&c);
    for (int i=1;i<=n;++i) scanf("%d",t+i);
    sort(t+1,t+n+1);//排序
    int l=0,r=1e10;
    while (l+1<r)
    {
        int mid=l+r>>1;
        if (check(mid)) r=mid;
        else l=mid;
    }//二分答案
    if (check(l)) printf("%d",l);
    else printf("%d",r);
    return 0;
} 

convention2

题面

虽然在接机上耽误了挺长时间,Farmer John为吃草爱好牛们举行的大会至今为止都非常顺利。大会吸引了世界各地的奶牛。
然而大会的重头戏看起来却给Farmer John带来了一些新的安排上的困扰。他的农场上的一块非常小的牧草地出产一种据某些识货的奶牛说是世界上最美味的品种的草。因此,所有参会的N头奶牛(1≤N≤105)都想要品尝一下这种草。由于这块牧草地小到仅能容纳一头奶牛,这很有可能会导致排起长龙。
Farmer John知道每头奶牛i计划到达这块特殊的牧草地的时间ai,以及当轮到她时,她计划品尝这种草花费的时间ti。当奶牛i开始吃草时,她会在离开前花费全部ti的时间,此时其他到达的奶牛需要排队等候。如果这块牧草地空出来的时候多头奶牛同时在等候,那么资历最深的奶牛将会是下一头品尝鲜草的奶牛。在这里,恰好在另一头奶牛吃完草离开时到达的奶牛被认为是“在等待的”。类似地,如果当没有奶牛在吃草的时候有多头奶牛同时到达,那么资历最深的奶牛是下一头吃草的奶牛。
请帮助FJ计算所有奶牛中在队伍里等待的时间(ai到这头奶牛开始吃草之间的时间)的最大值。

输入格式

文件名:convention2.in:
输入的第一行包含N。以下N行按资历顺序给出了N头奶牛的信息(资历最深的奶牛排在最前面)。每行包含一头奶牛的ai和ti。所有的ti为不超过104的正整数,所有ai为不超过109的正整数。

输出样例

文件名:convention2.out:
输出所有奶牛中的最长等待时间。

输入样例:

5
25 3
105 30
20 50
10 17
100 10

输出样例:

10
在这个例子中,我们有5头奶牛(按输入中的顺序编号为1..5)。奶牛4最先到达(时间10),在她吃完之前(时间27)奶牛1和奶牛3都到达了。由于奶牛1拥有较深的资历,所以她先吃,从她到达开始共计等待了2个单位时间。她在时间30结束吃草,随后奶牛3开始吃草,从她到达开始共计等待了10单位时间。在一段没有奶牛吃草的时间过后,奶牛5到达,在她正在吃草的时间里奶牛2也到达了,在5个单位时间之后能够吃到草。相比到达时间等待最久的奶牛是奶牛3。

题解

  • 到了初中以后再次受到了牛吃草问题的洗礼
  • 想到暴力模拟应该很简单,但是时间复杂度不优秀。我们选择数据结构维护。
  • 我们发现要求某些符合条件的最优值:即每一头牛[在它吃完草结束前到达]的[资历最深]的奶牛,即符合的条件是吃完草前到达,最优值是资历最深。最优值?这不就是优先队列嘛。因此我们可以用优先队列来维护。
  • 具体做法是:
    暴力找出第一个吃草的牛:按照到达时间为第一关键字,按照资历为第二关键字的顺序的第一个元素。然后将其放入堆中。
    如果堆是空的:新放进去。
    如果不为空:顺序查找来到时间小于这个牛吃草结束的牛,进堆;以经验为第一关键字。每一次的堆顶就是即将吃的草。
  • 预处理:预处理经验值,第i头可以认为是\(n-i+1\)
  • 时间复杂度:不高于\(O(nlogn)\)
  • 代码实现如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,ans=0;
struct node {
    int tim,exp,eat,lt;
}a[maxn];
inline bool cmp(node a,node b) {
    if (a.tim!=b.tim) return a.tim<b.tim;
    else return a.exp>b.exp;
}//排序
int main(void)
{
    freopen("convention2.in","r",stdin);
    freopen("convention2.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&a[i].tim);
        scanf("%d",&a[i].eat);
        a[i].exp=n-i+1;
    }
    sort(a+1,a+n+1,cmp);
    priority_queue < pair< int , int > > q;
    q.push(make_pair(a[1].exp,1));//用pair类型存储,经验为第一关键字
    int now=1,last_time=a[1].tim,cnt=0;
    while (now<=n)//now表示进堆元素
    {
        while (q.size())//堆为空
        {
            int p=q.top().second;q.pop();
            ans=max(ans,last_time-a[p].tim);//更新等待时间
            while (a[now+1].tim<=last_time+a[p].eat && now<n)
            {
                now++;
                q.push(make_pair(a[now].exp,now));
            }//将结束时间前的进堆
            last_time+=a[p].eat;//last_time存储上一头牛结束吃草的时间
        }
        now++,last_time=a[now].tim;
        q.push(make_pair(a[now].exp,now));//新进堆.如果now到n了也不要紧,下一层反正会退出
    }
    cout<<ans<<endl;
    return 0;
}

Mooyo Mooyo

题面

由于手上(更确实的,蹄子上)有大把的空余时间,Farmer John的农场里的奶牛经常玩电子游戏消磨时光。她们最爱的游戏之一是基于一款流行的电子游戏Puyo Puyo的奶牛版;名称当然叫做Mooyo Mooyo。
Mooyo Mooyo是在一块又高又窄的棋盘上进行的游戏,高\(N\)(\(1 \leq N \leq 100\))格,宽10格。 这是一个\(N = 6\)的棋盘的例子:
0000000000
0000000300
0054000300
1054502230
2211122220
1111111223
每个格子或者是空的(用0表示),或者是九种颜色之一的干草捆(用字符1..9表示)。重力会使得干草捆下落,所以没有干草捆的下方是0。
如果两个格子水平或垂直方向直接相邻,并且为同一种非0颜色,那么这两个格子就属于同一个连通区域。任意时刻出现至少\(K\)个格子构成的连通区域,其中的干草捆就会全部消失,变为0。如果同时出现多个这样的连通区域,它们同时消失。随后,重力可能会导致干草捆向下落入某个变为0的格子。由此形成的新的布局中,又可能出现至少\(K\)个格子构成的连通区域。若如此,它们同样也会消失(如果又有多个这样的区域,则同时消失),然后重力又会使得剩下的方块下落,这一过程持续进行,直到不存在大小至少为\(K\)的连通区域为止。
给定一块Mooyo Mooyo棋盘的状态,输出这些过程发生之后最终的棋盘的图案。

输入格式

文件名:mooyomooyo.in:
输入的第一行包含\(N\)和\(K\)(\(1 \leq K \leq 10N\))。以下\(N\)行给出了棋盘的初始状态。

输出格式

文件名:mooyomooyo.out:
输出\(N\)行,描述最终的棋盘状态。

输入样例:

6 3
0000000000
0000000300
0054000300
1054502230
2211122220
1111111223

输出样例:

0000000000
0000000000
0000000000
0000000000
1054000000
2254500000

在上面的例子中,如果K=3
,那么存在一个大小至少为K

的颜色1的连通区域,同样有一个颜色2的连通区域。当它们同时被移除之后,棋盘暂时成为了这样:

0000000000
0000000300
0054000300
1054500030
2200000000
0000000003

然后,由于重力效果,干草捆下落形成这样的布局:

0000000000
0000000000
0000000000
0000000000
1054000300
2254500333

再一次地,出现了一个大小至少为K

地连通区域(颜色3)。移除这个区域就得到了最终的棋盘布局:

0000000000
0000000000
0000000000
0000000000
1054000000
2254500000

题解

  • 大模拟...码农题...头很大啊...
    我们暂且叫它俄罗斯方块消消乐
  • 这道题分为3部:判断存在能消的方块→消掉方块→下落
    每一个都必须与dfs(flood-fiil)来模拟,因为方格的形状是不固定的。
  • 第一步:暴力求联通块,顺便求出和每一个联通块所在的联通块大小。有≥k的就继续,否则就结束循环。搞个死循环就好,让电脑慢慢搞
    第二部:消方块。根据第一步求出的数字,≥k的搞成0即可。
    第三部:下落。按照行数倒序枚举。最下那么先枚举到的一定是最底层的,叠在最小面并且从下往上叠。至于怎么叠?用一个数组标记即可。
  • 对于代码的解释:搜索的代码占了大部分,本质相同,因为具体操作不同所以分开来谢了。用函数写应该还是比较直观的了吧!
  • 代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,k;
int vd[1000];
int f[101][11];
int v[101][11];
int u[101][11];
int nm[101][11];
char a[101][11];
char temp[101][11];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,-1,1};
int num(int x,int y)
{
    int sum=1;
    v[x][y]=1;
    for (int i=0;i<4;++i)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if (nx>=1 && nx<=n && ny>=1 && ny<=10)
        if (!v[nx][ny] && a[x][y]==a[nx][ny]) sum+=num(nx,ny);
    }
    return sum;
}
//求x,y所在连通块大消息
void cover(int x,int y)
{
    u[x][y]=1;
    for (int i=0;i<4;++i)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if (nx>=1 && nx<=n && ny>=1 && ny<=10)
        if (!u[nx][ny] && a[x][y]==a[nx][ny])
        {
            nm[nx][ny]=nm[x][y];
            cover(nx,ny);
        }
    }
    return;
}
//求出某一个节点所在连通块大小后不断覆盖,节约时间
bool many(void)
{
    memset(v,0,sizeof(v));
    memset(u,0,sizeof(u));
    for (int i=1;i<=n;++i)
        for (int j=1;j<=10;++j)
            if (nm[i][j]==0)
            {
                nm[i][j]=num(i,j);
                cover(i,j);
            }
    for (int i=1;i<=n;++i)
        for (int j=1;j<=10;++j)
            if (nm[i][j]>=k && a[i][j]!='0') return true;
    return false;
}
//判断要不要继续消下去
void die(int x,int y)
{
    v[x][y]=1;
    char now=a[x][y];
    a[x][y]='0';
    for (int i=0;i<4;++i)
    {
        int nx=x+dx[i],ny=y+dy[i];
        if (nx>=1 && nx<=n && ny>=1 && ny<=10)
        if (!v[nx][ny] && now==a[nx][ny]) die(nx,ny);
    }
}
//消成0
void noit(void)
{
    for (int i=1;i<=n;++i)
        for (int j=1;j<=10;++j)
            if (a[i][j]!='0' && nm[i][j]>=k)
                die(i,j);
    return;
}
//枚举消的坐标
void downit(void)
{
    memset(temp,'0',sizeof(temp));
    for (int i=1;i<=100;++i) vd[i]=n+1;
    //掉落位置的预处理
    for (int i=n;i;--i)
        for (int j=1;j<11;++j)
            if (a[i][j]>'0') vd[j]--,temp[vd[j]][j]=a[i][j];
    //temp用来暂时存储图像
    for (int i=1;i<=n;++i)
        for (int j=1;j<11;++j)
            a[i][j]=temp[i][j];
    return;
}
//掉落
void print(void)
{
    for (int i=1;i<=n;++i)
        for (int j=1;j<=10;++j)
            if (j==10)
            {
                if (i==n) cout<<a[i][j];
                else cout<<a[i][j]<<'\n';
            }
            else cout<<a[i][j];
    cout<<endl;
}
//打印结果
int main(void)
{
    freopen("mooyomooyo.in","r",stdin);
    freopen("mooyomooyo.out","w",stdout);
    cin>>n>>k;
    for (int i=1;i<=n;++i)
        for (int j=1;j<=10;++j)
            cin>>a[i][j];
    while (many()==true)
    {
        memset(v,0,sizeof(v));
        memset(u,0,sizeof(u));
        noit();
        downit();
        memset(nm,0,sizeof(nm));
    }
    print();
    return 0;
}

原文地址:https://www.cnblogs.com/pigzhouyb/p/10124342.html

时间: 2024-10-13 15:23:22

USACO银组12月月赛题解的相关文章

FOJ 11月月赛题解

抽空在vjudge上做了这套题.剩下FZU 2208数论题不会. FZU 2205 这是个想法题,每次可以在上一次基础上加上边数/2的新边. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <queue

CodePlus2017 12月月赛 div2火锅盛宴

当时看到这道题感觉真是难过,我数据结构太弱啦. 我们来看看需要求什么: 1.当前熟了的食物的最小id 2.当前熟了的食物中有没有编号为id的食物 3.当前没熟的食物中有没有编号为id的食物 4.当前没熟的食物中编号为id的食物最接近煮熟的还需要多久才会熟 5.当前熟了的食物中编号在[l,r]之间的有多少个 我们需要维护的操作是: 1.往当前的锅里面加一个编号为id的生食物 2.每个时刻锅里面哪些生的食物要变熟 3.吃掉(删除)一个熟了的食物 感觉这种题真考人做题的毅力,求的东西好杂感觉脑子里都乱

csu-2018年11月月赛Round2-div1题解

csu-2018年11月月赛Round2-div1题解 A(2191):Wells的积木游戏 Description Wells有一堆N个积木,标号1~N,每个标号只出现一次 由于Wells是手残党,所以每次只能取出一块积木放在积木顶层 现在Wells想知道至少需要操作几次可以把积木堆成从顶至底标号升序 不论什么都很菜的Wells显然不知道怎么做 所以作为人生赢家的你义不容辞的决定帮助可怜的Wells Input 第一行一个正整数N 接下来N行,从顶至底描述每块积木的标号 Output 输出一行

csu-2018年11月月赛Round2-div2题解

csu-2018年11月月赛Round2-div2题解 A(2193):昆虫繁殖 Description 科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强.每对成虫过x个月产y对卵,每对卵要过两个月长成成虫.假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵(过X个月产卵),问过Z个月以后,共有成虫多少对?0=<X<=20,1<=Y<=20,X=<Z<=50 Input 单组数据 x,y,z的数值 Output 过Z个月以后,共有成虫对

bzoj 4836: [Lydsy2017年4月月赛]二元运算 -- 分治+FFT

4836: [Lydsy2017年4月月赛]二元运算 Time Limit: 8 Sec  Memory Limit: 128 MB Description 定义二元运算 opt 满足 现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问.每次询问给定一个数字 c 你需要求出有多少对 (i, j) 使得 a_i  opt b_j=c . Input 第一行是一个整数 T (1≤T≤10) ,表示测试数据的组数. 对于每组测试数据: 第一行是三个整数 n,m,q (

33.《高血压看这本就够了》摘录-陈罡——2019年12月22日

.bodyContainer { font-family: Arial, Helvetica, sans-serif; text-align: center; padding-left: 32px; padding-right: 32px; } .notebookFor { font-size: 18px; font-weight: 700; text-align: center; color: rgb(119, 119, 119); margin: 24px 0px 0px; padding:

NOIP提高组2004 合并果子题解

NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了.多多在合并果子时总共消耗的体力等于每次合并所耗体力之和. 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力.假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出

linux运维实战练习案例-2015年12月20日-12月31日 (第一次)

一.实战案例(练习)内容 假如你学习完Linux,想找一份儿Linux相关的运维工作,某天你接到一家公司给出的邀请,你来到该公司面试,面试前,运维主管给你出了一些简单的笔试题,题目如下: 1.创建一个10G的文件系统,类型为ext4,要求开机可自动挂载至单独数据/data目录: 操作步骤: (1).使用fdisk工具创建一个10G的分区 [[email protected] ~]# fdisk /dev/sda 欢迎使用 fdisk (util-linux 2.23.2). 更改将停留在内存中,

西电oj 6月月赛

西电oj 6月月赛 最近确实比较累,加上天气很热,宿舍一群人在打游戏,同时由于本身自己思维题做的不多,所以这次月赛自然而然的被虐了. 不过还是写写总结,顺便留坑以后补题解. A题,水题,但最近手速慢下来了,得多做题了. B题,水题,虽然题面看半天没懂,交了两次WA之后还是过了,看错题对手速狗来说是绝对不允许出现的,WA也不应该在能做的题出现. C题,这题很多人过了.这次没做出来只能说是运气不好,这种题做出来算自己运气好,做不出来算运气不好.这次这道题是用总情况减去掉异构的情况,最后得到答案,奇数