2017暑假训练第一场的一些题目

来源:互联网 发布:简历淘宝美工工作描述 编辑:程序博客网 时间:2024/06/07 02:03

2017暑假ACM训练第一场(石油大学主办)



B题:模拟

3635: 拯救小鸡

时间限制: 1 Sec  内存限制:128 MB
提交: 71  解决: 34
[提交][状态][讨论版]

题目描述

鸡国最近遇到了一件很棘手的事情,经常有一只老鹰想来抓小鸡。经鸡国情报员探查,这只老鹰打算共来袭击 n 次,第 i 次来的时刻为第 t i  (1≤i≤n)  秒时刻。 
鸡国国王为了保护鸡国中的小鸡,决定派出鸡国警察(鸡国有无穷多个警察)来巡逻。
每个警察巡逻的时间长度都为 t 秒。当老鹰来袭击的时刻至少要有 x 名警察才能抵御老鹰的袭击。另外国王派遣警察有两个原则: 
(1)每个时刻最多只能派遣一名警察。在第 0 秒时刻及第 0 秒之前的时刻(鸡国有负数时刻)也可以事先准备派遣警察,但每个时刻最多也只能派遣一名警察。 
(2)延迟 1 秒执行巡逻任务。第 i 秒时刻派遣的警察,在第 i+1 到 i+t 秒时刻执行巡逻任务。 
为帮助国王节省开支,请帮忙计算至少需要派遣多少名警察才能保证鸡国小鸡不被老鹰抓走? 

输入

输入共 2 行。 
第 1 行输入三个整数 n,t,x,分别表示老鹰总共袭击次数,每个警察巡逻的时间长度为,以及某个时刻能抵挡住老鹰袭击的最少警察数量。 
第 2 行 n 个严格升序排列的正整数 t i  (1≤i≤n),表示第 t i 秒时刻老鹰会发动袭击。 

输出

输出 1 行一个整数,表示总共至少需要派遣多少个警察才能抵御老鹰的 n 次袭击,如果出现无法抵御老鹰的袭击时,输出“-1”(不包含双引号)。 

样例输入

3 3 32 3 4

样例输出

5

【解析】:

对每一次袭击进行安排

树状数组维护时间轴

每一次袭击时刻y,开始安排左端点,然后y--,再安排,直到y时刻够人了,或者时间点不够了,就停

【代码】:

#include<stdio.h>  #include<string.h>  #include<string>  #include<iostream>  #include<algorithm>  #include<queue>  using namespace std;  int a[200000];int c[1010101];bool vis[1010101];int n,t,x,y; void add(int k,int num){    for(;k<=1010101;k+=k&-k)        c[k]+=num;}int read(int k){    int sum=0;    for(;k;k-=k&-k)        sum+=c[k];    return sum;}int main()  {    while(~scanf("%d%d%d",&n,&t,&x))      {          int sum=0;        memset(vis,0,sizeof(vis));        memset(c,0,sizeof(c));        for(int i=1;i<=n;i++)        {            scanf("%d",&y);            for(int time=y;read(y)<x&&time+t>y;time--)            {                if(!vis[time])                {                    sum++;                    if(time>0)                    {                        vis[time]=true;//添加起点                        add(time,1);                    }                    else add(1,1);                    add(time+t,-1);                }            }            if(read(y)<x){                sum=-1;                break;            }        }        printf("%d\n",sum);     }      return 0;  }  /**************************************************************    Problem: 3635    User: summer17083    Language: C++    Result: 正确    Time:8 ms    Memory:7408 kb****************************************************************/



C题:dp

3636: 母鸡下蛋

时间限制: 1 Sec  内存限制:128 MB
提交: 193  解决: 41
[提交][状态][讨论版]

题目描述

鸡国中的母鸡最擅长下蛋了,MGMG 是鸡国中一只以下蛋产量高而闻名全鸡国的母鸡。 
鸡国专供下蛋的 n 个鸡窝呈一字排列在鸡国的“下蛋中心”,从左到右依次编号为 1 到n。每个鸡窝都有一个最大可下蛋的量,其中第 i 个鸡窝的最大可下蛋量为 ci 。有时候由于MGMG 产量实在太大而无法在一个鸡窝中下完所有的蛋,不得不转移到隔壁的鸡窝继续下蛋,如果隔壁的鸡窝还是不能让它下完所有的蛋,则 MGMG 继续转移,直到下完所有的蛋,或者向“下蛋中心”管理员投诉“鸡窝数量实在太少了,我一只鸡的下蛋量都装不下!”。 
为了节省转移时所耗费的体力,请你编程帮助 MGMG 找若干个连续的鸡窝(个数尽量少),让它能下完所有的蛋。 

输入

输入共 2 行。 
第 1 行输入两个整数 n 和 t,表示“下蛋中心”有 n 个可供下蛋的鸡窝,MGMG 一次总共要下 t 个鸡蛋。 
第 2 行 n 个正整数 ci (1≤i≤n),依次表示第 i 个鸡窝最大可下蛋量为 ci 个。 

输出

输出 1 行一个整数或一个单词。当输出整数时表示让 MGMG 下完所有的蛋至少需要几个连续的鸡窝。当 MGMG 用完所有的鸡窝都无法下完所有的蛋时,MGMG 表示非常愤怒,输出单词“Angry”(不包含双引号,注意大小写)。 

样例输入

5 41 2 1 2 3

样例输出

2
【解析】:

题意寻找一个最短区间,能容下鸡蛋数。


用数组dp辅助 。dp[ i ] 表示前i项的和。

【代码】:

#include<stdio.h>#include<string.h>#include<iostream>#include<algorithm>using namespace std;int n,m;int dp[202000];int main(){while(~scanf("%d%d",&n,&m)){dp[0]=0;for(int i=1;i<=n;i++){int a;scanf("%d",&a);dp[i]=dp[i-1]+a;}if(dp[n]<m){printf("Angry\n");continue;}int len=20202020,r=0;//r记录左端点 for(int i=1;i<=n;i++)//i代表右端点 {while(i>r&&dp[i]-dp[r]>=m){if(dp[i]-dp[r]>=m&&len>i-r)len=i-r;r++;}}printf("%d\n",len);}return 0;}

G题:(卡特兰数,模板题)

3640: 【动态规划】cirs

时间限制: 1 Sec  内存限制:128 MB
提交: 124  解决: 48
[提交][状态][讨论版]

题目描述

Czyzoiers 都想知道小 x 为什么对鸡蛋饼情有独钟。经过一番逼问,小 x 道出了实情:因为他喜欢圆。最近小 x 又发现了一个关于圆的有趣的问题:在圆上有2N 个不同的点,小 x 想用 N 条线段把这些点连接起来(每个点只能连一条线段),使所有的线段都不想交,他想知道这样的连接方案有多少种?

输入

有且仅有一个正整数 N(N≤3000)。

输出

要求的方案数(结果 mod 100000007)。

样例输入

2

样例输出

2
【解析】:答案是卡特兰数列。

【代码】:

#include<stdio.h>#include<string.h>#include<iostream>using namespace std;int n,m;typedef long long ll;ll mod=100000007;ll a[33000];int main(){a[0]=a[1]=1;a[2]=2;for(int i=3;i<3030;i++){a[i]=0;for(int j=0,k=i-1;j<i;j++,k--){a[i]+=a[j]*a[k];}a[i]%=mod;}while(cin>>n){printf("%lld\n",a[n]);}return 0;}


H题:动态规划典型题

3641: wtaxi

时间限制: 1 Sec  内存限制:128 MB
提交: 50  解决: 24
[提交][状态][讨论版]

题目描述

话说小 x 有一次去参加比赛,虽然学校离比赛地点不太远,但小 x 还是想坐出租车去。大学城的出租车总是比较另类,有“拼车”一说,也就是说,你一个人坐车去,还是一堆人一起,总共需要支付的钱是一样的(每辆出租上除司机外最多坐下 4 个人)。刚好那天同校的一群 Oier 在校门口扎堆了,大家果断决定拼车去赛场。
问题来了,一辆又一辆的出租车经过,但里面要么坐满了乘客,要么只剩下一两个座位,众 Oier 都觉得坐上去太亏了,小 x 也是这么想的。
假设 N 位 Oier 准备拼车,此时为 0 时刻,从校门到目的地需要支付给出租车师傅 D 元(按车次算,不管里面坐了多少 Oier),假如 S 分钟后恰能赶上比赛,那么 S 分钟后经过校门口的出租车自然可以忽略不计了。现在给出在这 S 分钟当中经过校门的所有的 K 辆出租车先后到达校门口的时间 Ti 及里面剩余的座位 Zi(1 <= Zi <= 4),Oier 可以选择上车几个人(不能超过),当然,也可以选择上 0 个人,那就是不坐这辆车。
俗话说,时间就是金钱,这里小 x 把每个 Oier 在校门等待出租车的分钟数等同于花了相同多的钱(例如小 x 等待了 20 分钟,那相当于他额外花了 20 元钱)。
在保证所有 Oier 都能在比赛开始前到达比赛地点的情况下,聪明的你能计算出他们最少需要花多少元钱么?

输入

每组数据以四个整数 N , K , D , S 开始,具体含义参见题目描述。
接着 K 行,表示第 i 辆出租车在第 Ti 分钟到达校门,其空余的座位数为 Zi(时间按照先后顺序)。
N <= 100,K <= 100,D <= 100,S <= 100,1 <= Zi <= 4,1<= T(i) <= T(i+1) <= S

输出

对于每组测试数据,输出占一行,如果他们所有人能在比赛前到达比赛地点,
则输出一个整数,代表他们最少需要花的钱(单位:元),否则请输出“impossible”。

样例输入

2 2 10 51 12 2

样例输出

14
【解析】:

用数组dp[ j ]来表示:j个人时的费用,然后枚举车辆,不断更新数组dp


对于dp[ j ]的更新,就是比较坐上这辆车便宜,还是保持原状态。注意需要枚举人数,可能坐不满。


注意用数组pre提前记录下dp,保证原始数据。因为需要用原始数据去更新dp,而不能dp本身更新又更新,(联想一下01背包和完全背包的区别就明白了)

【代码】:

#include<stdio.h>#include<string.h>#include<iostream>#include<algorithm>using namespace std;int n,k,d,s;const int INF=0x3f3fffff;void init(int a[],int r){while(r--){a[r]=INF;}}int main(){while(cin>>n>>k>>d>>s){int dp[200],pre[200];init(dp,200);init(pre,200);dp[0]=0;//动态规划的开端数据 while(k--)//枚举每一辆车 {int t,z;cin>>t>>z;memcpy(pre,dp,sizeof(dp));//用pre暂存dp for(int j=0;j<=n;j++)//枚举总人数 {for(int p=0;p<=z;p++)//枚举上几个人 {if(j>=p&&dp[j]>pre[j-p]+d+p*t)dp[j]=pre[j-p]+d+p*t;}}}if(dp[n]!=INF)cout<<dp[n]<<endl;elseputs("impossible");  }return 0;}

I题(搜索+二维数组操作)

3606: 【搜索】泡泡龙

时间限制: 1 Sec  内存限制:64 MB
提交: 155  解决: 62
[提交][状态][讨论版]

题目描述

这是一个简化版的网络游戏:在一个N×N方块构成的棋盘中,每个方块均涂上红、黄、蓝、绿(记为l、2、3、4)中的一种颜色,游戏者可以在最底行任意找一个方块,用鼠标双击这个方块,于是该方块及与之相邻(即在上、下、左、右四个方向上有公共边)的所有的同色方块均被消掉,而因下方失去支持的方块将会自由落下填补空位。样例中给出一个4×4的棋盘样例,当游戏者双击最底层左边第二个方块后,将会形成输出结果的布局。
你的任务是编写一个泡泡龙模拟程序,对于给定的一个初始棋盘,计算游戏者双击最底层某个方块后棋盘的布局将会如何。

输入

第1行有两个正整数N和M(I≤M≤N≤I00),其中N表示棋盘的规模,而M则表示游戏者将双击最底层从左边数起的第M个方块。接下来的N行每行有N个l~4的整数组成,表示一个初始的棋盘,同一行相邻两个数之间用一个空格隔开。

输出

N行,每行用N个数给出游戏结束后棋盘的布局,没有方块的格子用0表示,同一行相邻两个数之间也用一个空格分开。每行末尾有空格

样例输入

4 21 2 3 44 2 4 43 4 4 31 4 4 3

样例输出

1 0 0 0 4 0 0 0 3 2 0 3 1 2 3 3 
【解析】:

先用dfs搜索把相同的位置清为0,然后二维数组操作。

【代码】:

#include<stdio.h>#include<string.h>#include<iostream>using namespace std;int n,m;int a[200][200];int vis[200][200];int dir[5][2]={0,1,0,-1,1,0,-1,0};//四个方向 void dfs(int i,int j){for(int k=0;k<4;k++){int x=i+dir[k][0];int y=j+dir[k][1];if(x>0&&y>0&&x<=n&&y<=n&& a[x][y]==a[i][j]&&!vis[x][y]){vis[x][y]=1;dfs(x,y);}}a[i][j]=0;}int main(){while(cin>>n>>m){memset(vis,0,sizeof(vis));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){scanf("%d",&a[i][j]);}dfs(n,m);for(int j=1;j<=n;j++)//枚举列{int d=n;for(int i=n;i>0;i--){while(d>0&&a[d][j]==0)d--;if(d<1)a[i][j]=0;elsea[i][j]=a[d--][j];}} for(int i=1;i<=n;i++){//输出 for(int j=1;j<=n;j++){printf("%d ",a[i][j]);}printf("\n");}}return 0;}