HNUCM2020年春季ACM集训队选拔赛(3)题解

问题 A: 手机键盘

题目描述

按照手机键盘输入字母的方式,计算所花费的时间 如:a,b,c都在“1”键上,输入a只须要按一次,输入c须要连续按三次。
若是连续两个字符不在同一个按键上,则可直接按,如:ad须要按两下,kz须要按6下。
若是连续两字符在同一个按键上,则两个按键之间须要等一段时间,如ac,在按了a以后,须要等一下子才能按c。
如今假设每按一次须要花费一个时间段,等待时间须要花费两个时间段。
如今给出一串字符,须要计算出它所须要花费的时间。node

输入

一个长度不大于100的字符串,其中只有手机按键上有的小写字母。c++

输出

输入可能包括多组数据,对于每组数据,输出按出Input所给字符串所须要的时间。程序员

样例输入

bob
wwwweb

样例输出

7
7数组

思路

在这里插入图片描述
这是一个拼音九键的图咱们知道了每个字符须要按的次数(每按一次须要1秒钟)
若是一个字符和它前一个字符所在的按键相等它就须要等待2秒
用一个数组a保存须要按的次数a[i]表示数字i须要按的次数
用一个数组b保存所在的按键的编号b[i]表示数组i所在按键的编号…
遍历一遍获得答案svg

#include<bits/stdc++.h>
using namespace std;
char s[105];
int main()
{ 
 
  
    int a[27]={ 
 
  0,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,4,1,2,3,1,2,3,4};
    int b[27]={ 
 
  0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,6,7,7,7,8,8,8,8};
    while(~scanf("%s",s+1)){ 
 
  
        int ls=strlen(s+1),ans=a[s[1]-'a'+1];
        for(int i=2;i<=ls;++i){ 
 
  
            if(b[s[i]-'a'+1]==b[s[i-1]-'a'+1]){ 
 
  
                ans+=2;
            }
            ans+=a[s[i]-'a'+1];
        }
        printf("%d\n",ans);
    }
}

问题 B: 买房

题目描述

某程序员开始工做,年薪N万,他但愿在中关村公馆买一套60平米的房子,如今价格是200万。
假设房子价格以每一年百分之K增加,而且该程序员将来年薪不变,且不吃不喝,不用交税。
每一年所得N万全都积攒起来,问第几年可以买下这套房子(第一年房价200万,收入N万)。函数

输入

有多行,每行两个整数N(10<=N<=50), K(1<=K<=20)。测试

输出

针对每组数据,若是在第21年或者以前就能买下这套房子,则输出一个整数M,表示最先须要在第M年能买下,不然输出Impossible,输出须要换行。spa

样例输入

50 10
40 10
40 8.net

样例输出

8
Impossible
10

思路

每年计算出房子的价格和这我的所挣的钱进行比较
若是在21年前工资大于等于房子价格说明能购买,反之不能

#include<bits/stdc++.h>
using namespace std;
char s[105];
int main()
{ 
 
  
    int n,k;
    while(~scanf("%d %d",&n,&k)){ 
 
  
        int ans=0;
        double ad=0.01*k,a=200,b=n;
        for(int i=1;i<=21;++i,b+=n,a+=a*ad){ 
 
  
            if(a<=b){ 
 
  
                ans=i;
                break;
            }
        }
        if(ans){ 
 
  
            printf("%d\n",ans);
        }else{ 
 
  
            printf("Impossible\n");
        }
    }
}

问题 C: 与7相关的数

题目描述

一个正整数,若是它能被7整除,或者它的十进制表示法中某个位数上的数字为7, 则称其为与7相关的数。
现求全部小于等于n(n<100)的与7无关的正整数的平方和。

输入

案例可能有多组。对于每一个测试案例输入为一行,正整数n,(n<100)。

输出

对于每一个测试案例输出一行,输出小于等于n的与7无关的正整数的平方和。

样例输入

21

样例输出

2336

思路

按题目要求计算就行

#include<bits/stdc++.h>
using namespace std;
bool ju(int x){ 
 
  
    if(x%7==0){ 
 
  
        return false;
    }
    while(x){ 
 
  
        if(x%10==7){ 
 
  
            return false;
        }
        x/=10;
    }
    return true;
}
int main()
{ 
 
  
    int n;
    while(~scanf("%d",&n)){ 
 
  
        int ans=0;
        for(int i=1;i<=n;++i){ 
 
  
            if(ju(i)){ 
 
  
                ans+=i*i;
            }
        }
        printf("%d\n",ans);
    }
}

问题 D: 采药

题目描述

辰辰是个颇有潜能、天资聪颖的孩子,他的梦想是称为世界上最伟大的医师。
为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。
医师把他带到个处处都是草药的山洞里对他说: “孩子,这个山洞里有一些不一样的草药,采每一株都须要一些时间,每一株也有它自身的价值。
我会给你一段时间,在这段时间里,你能够采到一些草药。
若是你是一个聪明的孩子,你应该可让采到的草药的总价值最大。”
若是你是辰辰,你能完成这个任务吗?

输入

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),T表明总共可以用来采药的时间,M表明山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间(包括1和100)的的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出

可能有多组测试数据,对于每组数据,输出只包括一行,这一行只包含一个整数,表示在规定的时间内,能够采到的草药的最大总价值。

样例输入

70 3
71 100
69 1
1 2

样例输出

3

思路

裸的01背包问题
此处是肖鹏学长的01背包博客

#include<bits/stdc++.h>
using namespace std;
int a,b,dp[1005];
int main()
{ 
 
  
    int t,m;
    while(~scanf("%d %d",&t,&m)){ 
 
  
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=m;++i){ 
 
  
            scanf("%d %d",&a,&b);
            for(int j=t;j>=a;--j){ 
 
  
                dp[j]=max(dp[j],dp[j-a]+b);
            }
        }
        printf("%d\n",dp[t]);
    }
}

问题 E: 鸡兔共笼

题目描述

一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。
已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。

输入

每组测试数据占1行,每行一个正整数a (a < 32768)。

输出

输出包含n行,每行对应一个输入,包含两个正整数,第一个是最少的动物数,第二个是最多的动物数,两个正整数用一个空格分开。
若是没有知足要求的答案,则输出两个0。

样例输入

2
3
20

样例输出

0 0
5 10

思路

若是脚的个数是奇数说明没有答案
不然存在答案
最少的脚的个数大于等于4固然是优先算兔子,若是最后还剩下有就用鸡补上
最多的脚数的状况就是都是鸡
也就是说有三种状况
1.脚是奇数–没有答案
2.脚数是偶数但不能被四整除–答案就是(n/4+1,n/2)
3.脚数能被四整除–答案就是(n/4,n/2)

#include<bits/stdc++.h>
using namespace std;
int main()
{ 
 
  
    int n,m;
    while(~scanf("%d",&n)){ 
 
  
        for(int i=1;i<=n;++i){ 
 
  
            scanf("%d",&m);
            if(m&1){ 
 
  
                printf("0 0\n");
            }else if(m%4){ 
 
  
                printf("%d %d\n",m/4+1,m/2);
            }else{ 
 
  
                printf("%d %d\n",m/4,m/2);
            }
        }
    }
}

问题 F: 为了虫群

题目描述

小 Y 率领的人类的部队已经踏上了异虫的基地——查尔星。 做为异虫的首脑,你决定派出爆虫来迎击人类的部队。

爆虫身上长有充满酸液的囊。当它接近敌人时,会触发体内的不稳定化学物质进行反应,将自身引爆,向外泼洒强酸。自爆会摧毁爆虫,但同时也会对半径 R 以内(包括 距离为 R 的点 )的敌人形成大量伤害。

你观察到,人类有 n 名陆战队员,站成一条直线。 每一个陆战队员的坐标是 xi。你有 k 个爆虫。爆虫的酸液会对陆战队员形成巨大伤害,将其瞬间消灭。 你能够把每只爆虫安排在直线上的任意位置,而后引爆,从而消灭距离该爆虫不超过 R 的全部陆战队员。

为了消灭全部 n 个陆战队员,你须要计算,爆虫的爆炸半径 R 至少要多少。

输入

输入共两行。第一行是用空格隔开的两个整数 n 和 k。

第二行是用空格隔开的 n 个实数,表示每一个陆战队员的坐标 xi。全部坐标按升序给出。(可能存在某个位置有多名队员)
1≤k<n≤100,000

-107≤xi≤107

输出

输出共一行。爆虫的最小爆炸半径 R。保留 6 位小数。

样例输入

5 2
-10.0 -6.0 10 11 15

样例输出

2.500000

思路

由于爆虫的半径对知足单调性
也就是说
对于爆虫的半径a<b 若是b能够那么a也能够,若是a不能够,那么b也不能够
能够二分答案不断缩小可能区间,缩小到必定精度就能够跳出了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
double a[maxn];
int n,k;
bool ju(double mid){ 
 
  //判断半径为mid可不能够知足
    double len=0;
    int num=1;
    for(int i=2;i<=n;++i){ 
 
  //循环判断mid半径下最少须要的虫子数
        if(len+a[i]-a[i-1]<=mid){ 
 
  
            len+=a[i]-a[i-1];
        }else{ 
 
  
            len=0;
            ++num;
        }
    }
    if(num>k){ 
 
  //须要的虫子大于k说明不知足
        return false;
    }
    return true;
}
int main()
{ 
 
  
    while(~scanf("%d %d",&n,&k)){ 
 
  
        for(int i=1;i<=n;++i){ 
 
  
            scanf("%lf",&a[i]);
        }
        double l=0,r=a[n]-a[1],ans=a[n]-a[1];
        while(r-l>0.00000001){ 
 
  
            double mid=(l+r)/2;
            if(ju(mid*2)){ 
 
  
                ans=mid;
                r=mid;
            }else{ 
 
  
                l=mid;
            }
        }
        printf("%lf\n",ans);
    }
}

问题 G: 找宝箱

题目描述

做为一个强迫症患者,小明在走游戏里的迷宫时必定要把全部的宝箱收集齐才肯罢休。

如今给你一个 N×M 的迷宫,里面有障碍、空地和宝箱,小明在某个起始点,每一步小明能够往上、下、左、右走,固然前提是没有走出迷宫而且走到的点不是障碍。若是小明走到了某个为宝箱的点,那么这个宝箱就被他收集到了,而后此处变为空地。

如今你须要计算小明最少须要走多少步才能收集齐全部的宝箱。

输入

输入共有两行。

第一行一个两个正整数 N 和 M,表示迷宫大小。

接下来 N 行,每行 M 个整数,第 i+1 行的第 j 个整数表示迷宫第 i 行第 j 列的状况, 0 表示空地,-1 表示障碍,1 表示宝箱,2 表示小明的起始点。保证 2 只有一个。

1≤N,M≤100,宝箱个数不超过5个。

输出

一行一个整数,表示小明最少的步数。若是没法收集齐全部宝箱,输出-1。

样例输入

3 5
1 -1 1 -1 2
0 -1 0 -1 0
0 0 0 0 0

样例输出

12

思路

由于宝箱的个数很少,能够用bfs处理出宝箱和起点两两之间的最短距离
再用全排列函数(next_permutation)或者dfs列出全部宝箱的访问顺序,计算步数取最小值

#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int w[105][105],vis[105][105],n,m,cnt;
int dir[4][2]={ 
 
  1,0,-1,0,0,1,0,-1};
struct node{ 
 
  
    int i,j,step;
};
node chest[6];
int len[6][6],p[6];
void bfs(int st,int step){ 
 
  
    memset(vis,0,sizeof(vis));
    queue<node> pq;
    node now=chest[st],ne;
    now.step=0,vis[now.i][now.j]=1;
    pq.push(now);
    while(!pq.empty()){ 
 
  
        now=pq.front(),pq.pop();
        if(w[now.i][now.j]==1||w[now.i][now.j]==2){ 
 
  //走到宝箱或者起点
            for(int i=0;i<=cnt;++i){ 
 
  
                if(chest[i].i==now.i&&chest[i].j==now.j){ 
 
  
                    len[st][i]=now.step;
                    break;
                }
            }
        }
        for(int i=0;i<4;++i){ 
 
  
            ne.i=now.i+dir[i][0];
            ne.j=now.j+dir[i][1];
            if(ne.i>=1&&ne.i<=n&&ne.j>=1&&ne.j<=m&&vis[ne.i][ne.j]==0&&w[ne.i][ne.j]!=-1){ 
 
  //没有出界,没有被走过,能够走
                vis[ne.i][ne.j]=1;
                ne.step=now.step+1;
                pq.push(ne);
            }
        }
    }
}
int main(){ 
 
  
    while(~scanf("%d %d",&n,&m)){ 
 
  
        cnt=0;
        memset(len,inf,sizeof(len));
        for(int i=1;i<=n;++i){ 
 
  
            for(int j=1;j<=m;++j){ 
 
  
                scanf("%d",&w[i][j]);
                if(w[i][j]==1){ 
 
  
                    chest[++cnt].i=i;
                    chest[cnt].j=j;
                }
                if(w[i][j]==2)
                    chest[0].i=i,chest[0].j=j;
            }
        }
        for(int i=0;i<=cnt;++i){ 
 
  //处理宝箱和起点两两间的距离
            bfs(i,0);
        }
        for(int i=0;i<=cnt;++i)
            p[i]=i;
        int ans=inf;
        do{ 
 
  
            int num=0;
            for(int i=1;i<=cnt;++i)
                num+=len[p[i-1]][p[i]];
            ans=min(ans,num);
        }while(next_permutation(p+1,p+cnt+1));//全排列函数
        if(ans>=inf)
            ans=-1;
        printf("%d\n",ans);
    }
}

问题 H: gcd 区间

题目描述

给定一行 n 个正整数 a[1]…a[n]。

m 次询问,每次询问给定一个区间[L,R],输出 a[L]…a[R]的最大公因数。

输入

第一行两个整数 n,m。

第二行 n 个整数表示 a[1]…a[n]。

如下 m 行,每行 2 个整数表示询问区间的左右端点。

保证输入数据合法。

1 <= n <= 1000,1 <= m <= 1,000,000 0 < 数字大小 <= 1,000,000,000

输出

共 m 行,每行表示一个询问的答案。

样例输入

5 3
4 12 3 6 7
1 3
2 3
5 5

样例输出

1
3
7

思路

n的范围不大,能够先预处理出全部状况,最后直接输出就行,固然线段树和树状数组也可

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[1005],ans[1005][1005];
ll gcd(ll a,ll b){ 
 
  
    if(b==0)
        return a;
    return gcd(b,a%b);
}
int main(){ 
 
  
    int n,m;
    while(~scanf("%d %d",&n,&m)){ 
 
  
        for(int i=1;i<=n;++i){ 
 
  
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=n;++i){ 
 
  
            ans[i][i]=a[i];
            for(int j=i+1;j<=n;++j){ 
 
  
                ans[i][j]=gcd(a[j],ans[i][j-1]);
            }
        }
        int l,r;
        for(int i=1;i<=m;++i){ 
 
  
            scanf("%d %d",&l,&r);
            printf("%lld\n",ans[l][r]);
        }
    }
}