2020“华为杯”中国矿业大学程序设计竞赛现场赛(提高组)(题解)

问题 A: 购物停车
题目描述
小明每周都要开车陪女朋友去商业街购物,商业街是一条笔直的大街,我们把它抽象成是X轴上的一条线段,街上的每个商铺都位于线段上整数点的位置,而顾客可以在线段上的任何一个位置停车,并且每停一次车都需要交一次费用。于是小明想知道如果自己就停一次车,然后步行去之前计划好要去的那些商铺,最后回到停车的位置,那么最短的步行路程是多少。
输入
输入的第一行为一个整数t(1<=t<=100),表示测试样例的组数。
接下来输入t组样例,每组输入占两行,第一行输入一个整数n(1<=n<=20),表示小明他们计划要去的商铺的个数,第二行输入n个整数,表示计划要去的那些商铺的位置,位置范围为[0,99]。
输出
对于每组输入,输出题目要求的最短的步行路程。
样例输入
2
4
24 13 89 37
6
7 30 41 14 39 42
样例输出
152
70

将车停在正中间,答案为2*最长长度

#include
using namespace std;
#define ll long long
int main()
{
    ios::sync_with_stdio(false);
    int n,t,mx,mn,tmp;
    cin>>t;
    while(t--)
    {
        cin>>n;
        mx=-1;mn=100;
        for(int i=1;i<=n;i++)
        {
            cin>>tmp;
            mx=max(mx,tmp);
            mn=min(mn,tmp);
        }
        cout<<2*(mx-mn)<

问题 B: Brannn爱数学
题目描述
通常,正整数 n 的阶乘是所有小于或等于 n 的正整数的乘积。

大数学家Brannn设计了一个笨阶乘 clumsy:在整数的递减序列中,以一个固定顺序的操作符序列来依次替换原有的乘法操作符:乘法(*),除法(/),加法(+)和减法(-)。

例如,clumsy(11) = 11 * 10 / 9 + 8 - 7 * 6 / 5 + 4 - 3 * 2 / 1。然而,这些运算仍然使用通常的算术运算顺序:另外,Brannn使用的除法是地板除法(floor division),所以 11 * 10 / 9 等于 12。这保证结果是一个整数。

输入
多组输入,每行只有一个整数 N(1<=N<=10000)
输出
对于每组输入,输出N 的笨阶乘
样例输入
1
4
11
样例输出
1
7
10

模拟

#include
using namespace std;
#define ll long long
int main()
{
    ios::sync_with_stdio(false);
    int n,t,mx,mn,tmp;
    while(cin>>n)
    {
        int sum=0,t=0,bo=0;
        if(n==3) 
        {
            cout<<6<=1;)
        {
         if(i>=3) t=i*(i-1)/(i-2);
         else if(i>2) t=i*(i-1);
         else t=i;
         sum-=t;
         if(i>3) sum+=i-3;
         i-=4;
        }
        cout<

问题 C: Brannn爱小猪
题目描述
Brannn 有 n 只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在 m 分钟内死去。

问题来了,如果 Brannn 需要在 p 分钟内,弄清楚哪只水桶含有毒药,Brannn最少需要多少只猪?

可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。

小猪喝完水后,必须有 m 分钟的冷却时间。在这段时间里,只允许观察,而不允许继续饮水。
任何给定的桶都可以无限次采样(无限数量的猪)。

输入
多组输入。

第一行为测试组数T (1<=T<=500000) 。

接下来T行,每行包括水桶数 n ,小猪喝毒死亡的时间 m 以及 Brannn 完成实验所需要的时间 p (1<= n,m,p<=20000) 。

输出
对于每组输入,输出最少所需要猪的数量。
样例输入
2
4 3 3
3 3 6
样例输出
2
1
如果 p / n = 0,那么每一只猪只有一种状态,即存活。
如果 p / n = 1,那么每一只猪有两种状态,存活或者死亡。
进一步而言,如果 p / n = 2,那么每一只猪有三种状态,存活、在第一次测试后死亡、在第 二次测试后死亡。
因此每一只猪的状态数量为 s = p / n + 1。

分析:x 只 2 状态的猪最多可以测试多少个水桶?
1 只猪可以测试 1 个水桶:让它喝下桶 1 的水,如果存活说明 2 号桶是毒药,如 果死亡说明 1 号桶是毒药。
同理,2 只猪可以测试 4 个水桶。更一般地,x 只猪可以测试 2^x 个水桶。
同样可以推导出 x 只 s 状态的猪最多可以测试 s^x 个水桶。
因此我们需要找到最小的 x,使得 s^x ≥ n ,其中 s = p / n + 1 表示每只猪的状态数。
因此答案为 x≥logsn。根据换底公式,我们可以得到:x≥logn/logs

#include 
using namespace std;
int t,n,m,p;
int main()
{
    scanf("%d",&t);
    while(t--)
	{
        scanf("%d%d%d",&n,&m,&p);
        int s=(p/m)+1;
        printf("%d\n",(int)ceil(log(n)/log(s)));
    }
    return 0;
} 

问题 D: Halobin的序列
题目描述
Halobin有一个01序列,他每次可以翻转一个元素,即将该元素从0变1或者从1变0。

现在他希望序列不降,求最少翻转次数。

输入
第一行输入一个数n,表示字符串的长度。其中1≤n≤20000;
第二行输入一个由‘0’和‘1’组成的字符串
输出
输出一个非负整数,表示最少翻转次数
样例输入
6
010110
样例输出
2

预先处理出来前缀中 1 的个数和后缀中 0 的个数
出现1 0时
可将0->1,此时后面的数都需修改为1
可将1->0,此时前面的数都需修改为0

#include
using namespace std;
#define ll long long
int q[20100],h[20100];
int main()
{
    ios::sync_with_stdio(false);
    int n,len,mn=20010,sum=0;
    string s;
    cin>>n>>s;
    len=s.length();
    q[0]=0;h[len-1]=0;
    for(int i=1;i=0;i--)
    {
        if(s[i+1]=='0') h[i]=h[i+1]+1;
        else h[i]=h[i+1];
    }
    int tip=0;
    for(int i=1;i

问题 E: 数组构造
题目描述
一天,小陈想出了一个巧妙的数组构造问题,于是想考考小宇。

给出一个长度为n的数组a,需要构建一个相同长度的数组b。其中,对于数组a中的第i个元素ai,要确保ai是除去数组b中前i个数后最小的非负数。此外,小陈提出了几个限制条件:

1、数组b中各个数均不相同,且都为非负数;

2、使得构造的数组b的所有数之和最小;

3、如果存在多个数组,选择排列后最小的数组(即如果a=[0,0,0],则b有[1,2,3]、[1,3,2]等等,其中[1,2,3]最小)。

小宇想了很久,还是没有头绪。你能帮助他吗?

输入
第一行为T(0≤T≤20),代表输入T个样例。

对于每个样例,第一行输入数组长度n(1≤n≤105),第二行输出数组a的n个数(0≤ai≤i,1≤i≤n),按非严格递增排序。

输出
一共T行,每行代表一个样例结果。注意每行结尾没有空格。
样例输入
2
3
1 2 3
3
1 1 3
样例输出
0 1 2
0 2 1

当 ai≠ai-1时,bi= ai-1。
对于剩下的 bi,从 0 开始依次填 充。注意,填充时要除去 an和已填进去的数。

#include
using namespace std;
#define ll long long
int a[100100],b[100100],c[100100];
int main()
{
 int t,n;
 scanf("%d",&t);
 while(t--)
 {
  memset(c,0,sizeof(c));
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  {
  	scanf("%d",&a[i]);
  	b[i]=-1;
  }
  c[a[n]]=1;
  for(int i=2;i<=n;i++)
   if(a[i]!=a[i-1])
   {
   	b[i]=a[i-1];
   	c[a[i-1]]=1;
   }
  int tip=0;
  for(int i=1;i<=n;i++)
   if(b[i]==-1)
   {
   	while(c[tip]!=0) tip++;
   	b[i]=tip;
   	tip++;
   }
   printf("%d",b[1]);
   for(int i=2;i<=n;i++)
    printf(" %d",b[i]);
   if(t) printf("\n");
 } 
 return 0;	
} 

问题 F: 求2位自然数的个数
题目描述
自然数N是一个两位数,它是一个质数,且N的个位数字和十位数字都是质数,请编程输出这样的自然数的个数?
输入
无输入数据。
输出
直接编程输出符合题意的自然数个数。

提示:1不计入

问题 G: Brannn爱旅游
题目描述
Brannn 热爱旅游,在大学四年里一共游览了 n 个不同的景区,并对每个景区评分,其中第 i 个景区的评分为 a[i],现在 Brannn 想从这 n 个景区中挑出其中的 2 个,使得这两个景区的组合得分最高。

景区 i 和 景区 j (i

请你帮 Brannn 选择一对景区,使得它们能够取得的组合得分最高。

输入
单组输入,第一行为一个整数 n 表示景区的数量。(2<=n<=50000)
第二行一共 n 个整数 a[0],a[1],…,a[n-1],分别对应 n 个景区的评分。(1<=a[i]<=20000)
输出
输出一个整数,表示能够取得的最高组合得分。
样例输入
5
8 1 5 2 6
样例输出
11
先算一遍后缀(a[j]-j)的最大值
在来一遍算(a[i]+i+后缀最大值)

#include
using namespace std;
#define ll long long
int a[50010],h[50010];
int main()
{
    ios::sync_with_stdio(false);
    int n,mx=-100000000;
    cin>>n;
    for(int i=1;i<=n;i++)
     cin>>a[i];
    h[n]=-10000000;
    for(int i=n-1;i>=1;i--)
    {
        h[i]=max(h[i+1],a[i+1]-(i+1));
    }
    /*for(int i=1;i<=n;i++)
     cout<

问题 H: 啊冷种树
题目描述
阿冷喜欢种树,但他有个奇怪的爱好,他喜欢将连续相同种类的树当成一片森林。
(比如trees=[0,1,1,2,2,2,1],它包含4片森林,[{0}, {1,1}, {2,2,2}, {1}])

现在有m颗树,有n种树的种类(数的种类从1到n)
并给出在位置i,种植种类为j的树所消耗的力气 cost[i][j]
求阿冷最终将m颗树种植成k片森林的最少消耗的力气。
输入
第一行有一个参数T, 代表有T组样例

接下来一行 有三个参数 m, n, k 代表 m颗树, n种树,以及最终需要的森林数
接下来有m行,每行有n个参数,分别代表在位置i, 种植种类为j的树所消耗的力气。

1 <= T <= 5
1 <= m <= 100
1 <= n <= 10
1 <= k <= m
1 <= cost[i][j] <= 50

输出
对于每一个样例,每行输出最少消耗的力气,不存在的情况输出-1

样例输入
1
2 2 1
2 5
1 3
样例输出
3
提示
2颗树 2种树 最后需要生成一片森林

第一颗树 种植为种类1 所需要的力气为 2

第一颗树 种植为种类2 所需要的力气为 5

第二颗树 种植为种类1 所需要的力气为 1

第二颗树 种植为种类2 所需要的力气为 3

因而形成一片森林 所需要的最少力气为3 (即将第一棵树和第二颗都种植种类为1的树)

dp[i][j][k]: 到第i棵树,选择j种方案,形成k片树林的最小开销
初始化:
dp[1][i][1]=cost[1][i];
转换:
同一种:dp[i][no][m]=min(dp[i][no][m],dp[i-1][pr][m]+cost[i][no]);
不同:dp[i][no][m]=min(dp[i][no][m],dp[i-1][pr][m-1]+cost[i][no]);

#include
using namespace std;
#define ll long long
int dp[110][15][110],cost[110][15];
int main()
{
 int t,m,n,k;
 scanf("%d",&t);
 while(t--)
 {
  cin>>m>>n>>k;
  for(int i=1;i<=m;i++)
   for(int j=1;j<=n;j++)
   cin>>cost[i][j];
  memset(dp,0x3f3f3f3f,sizeof(dp));
  for(int i=1;i<=n;i++)
   dp[1][i][1]=cost[1][i];
  for(int i=2;i<=m;i++)
   for(int pr=1;pr<=n;pr++)
    for(int no=1;no<=n;no++)
     for(int m=1;m<=k;m++)
     {
     	if(pr==no)
     	 dp[i][no][m]=min(dp[i][no][m],dp[i-1][pr][m]+cost[i][no]);
     	else
     	 dp[i][no][m]=min(dp[i][no][m],dp[i-1][pr][m-1]+cost[i][no]);
	 }
    int mn=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
     mn=min(mn,dp[m][i][k]);
    if(mn<0x3f3f3f3f) cout<

问题 I: 九宫格手势
题目描述
阿冷爱玩手机,突然有一天,他想计算一下手机9宫格手势的方案数。

1 2 3
4 5 6
7 8 9

简单起见,我们约定手势不可以跨越,只能连续,不可以重复经过一个点

(1->5->9是可以的,因为相邻两点均连续)

(4->6是不可以的,因为两点不连续)
(1->2->4->1是不可以的,因为重复经过了点1)

手机9宫格手势有多少种?
输入
无需输入
输出
求手势长度为1,2,3,4,5,6,7,8,9的方案数。
每个方案数存一行。
提示

手势长度为1的方案数有9种

手势长度为2的方案数有40种

dfs

#include
using namespace std;
#define ll long long
int g[4][4],sum=0;
int dir[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
void dfs(int x,int y,int aim,int ans)
{
	if(ans==aim) 
	{
	 sum++;return;	
	}
	for(int i=0;i<8;i++)
	{
		int tx=x+dir[i][0];
		int ty=y+dir[i][1];
		if(tx>=1&&tx<=3&&ty>=1&&ty<=3&&g[tx][ty]==0)
		{
			g[tx][ty]=1;
			dfs(tx,ty,aim,ans+1);
			g[tx][ty]=0;
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
    for(int len=1;len<=9;len++)
    {
    	sum=0;
    	for(int i=1;i<=3;i++)
    	 for(int j=1;j<=3;j++)
    	 {
    	 	memset(g,0,sizeof(g));
    	 	g[i][j]=1;
    	 	dfs(i,j,len,1);
    	 	g[i][j]=0;
    	 }
    	cout<

问题 J: xiaok的数组
题目描述
作为King of array,xiaok非常喜欢研究数组。今天他将数组的K值定义如下:给定数组a和整数x,你可以在a中选择一个子段(子段可以为空),将其中的元素都乘以x,得到数组A。显然选择不同的子段会得到不同的数组A。这些数组A的最大子段和的最大值就是a的K值。(数组的最大子段和就是在这个数组中找到连续的任意个元素的和的最大值。例如[10, -5, 10, -4, 1]的最大子段和是15,对应的子段为[10, -5, 10];数组[-1,-4]的最大子段和为0,对应的子段为[ ])
xiaok让Brannn来求数组的K值,但是Brannn根本不会,你能帮帮他吗?
输入
单组输入。第一行包括数组a的长度n以及整数x。(1≤n≤3⋅105,−100≤x≤100)
第二行包含n个数a1,a2,…,an 表示a的n个元素。(−109≤ai≤109)
输出
输出一个整数,表示数组a的K值。
样例输入
5 -2
-3 8 -2 1 -6
样例输出
22
提示

对于样例输入,将子段[-2,1,-6]乘以-2,数组变成[-3,8,4,-2,12],求出最大子段和得到K值为22

将这个数列分为 3 段考虑,第一段和第三段是未修改的,第二段是修改的子段

设 dp[i][j]为第 i 个数字为第 j+1 段的最大子段和

三种转移方程

第一段:dp[i][0]=max(a[i],dp[i−1][0]+a[i])
第二段:dp[i][1]=max(a[i]∗x,dp[i−1][0]+a[i]∗x,dp[i−1][1]+a[i]∗x)
第三段:dp[i][2]=max(a[i],dp[i−1][0]+a[i],dp[i−1][1]+a[i],dp[i−1][2]+a[i])

#include
using namespace std;
#define ll long long
ll dp[300100][3],a[300100];
int main()
{
 int n,x;
 scanf("%d %d",&n,&x);
 for(int i=1;i<=n;i++)
  scanf("%lld",&a[i]);
 ll ans=0;
 for(int i=1;i<=n;i++)
 {
 	dp[i][0]=max((ll)0,dp[i-1][0])+a[i];
 	dp[i][1]=max((ll)0,max(dp[i-1][0],dp[i-1][1]))+a[i]*x;
 	dp[i][2]=max((ll)0,max(dp[i-1][2],dp[i-1][1]))+a[i];
 	ans=max((ll)0,max(max(dp[i][0],dp[i][1]),dp[i][2]));
 }
 printf("%lld",ans);
 return 0;	
} 

你可能感兴趣的:(2020“华为杯”中国矿业大学程序设计竞赛现场赛(提高组)(题解))