ACM第二次考核题解
难度 | 题目编号 | 题目 | 考察知识点 |
---|---|---|---|
简单 | C | This is an English question! | 无 |
简单 | G | ACM在哪里? | 字符串 |
简单 | A | 2,4,8,16 | 进制转换 |
简单 | J | 水仙花数 | 水仙花数 |
一般 | F | Simple Addition——简单加法 | 高精度 |
一般 | H | 抓小偷 | 快速幂 |
一般 | B | 聚餐 | 排序 |
一般 | I | HH学长的名单 | 二分 |
一般 | D | 短板 | 双指针 |
困难 | K | 我要子矩阵puls! | 二维前缀和 |
困难 | E | Simple Sequence——简单序列和 | 二分应用+思维+前缀和 |
A 2,4,8,16
按照题意模拟即可
#include <stdio.h>
int main(){
char m[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char s[105];
scanf("%s",&s);
int a,b,c,i;
for(i=0;s[i]!='\0';i+=10){
a=(s[i+1]-'0')*2+(s[i+2]-'0');
b=(s[i+3]-'0')*4+(s[i+4]-'0')*2+(s[i+5]-'0');
c=(s[i+6]-'0')*8+(s[i+7]-'0')*4+(s[i+8]-'0')*2+(s[i+9]-'0');
printf("%c%c%c%c",s[i],m[a],m[b],m[c]);
}
return 0;
}
B 聚餐
此题只需要找到答案把答案存起来,然后排序答案输出。
如果你先按照菜品价格排序了,那就无法知道他原来的序号了,需要一个结构体存储,这样可能就需要用到第三次培训会讲到的结构体排序啦。
#include<stdio.h>
int a[2100],ans[2100];
int st[2100];//存第i个菜被选没
int main()
{
int idx = 0;//表示答案ans数组的长度
int n,k;
scanf("%d%d",&n,&k);
for(int i = 0 ; i < n ; i ++) scanf("%d",&a[i]);
for(int i = 0;i < k ; i ++)
{
//枚举每一轮选菜 共k伦
int t = -1;
for(int j = 0 ; j < n ; j ++)
{
//寻找没有被选并且最大喜爱度的菜
if(!st[j] && (t == -1 || a[j] > a[t])) t = j;
}
st[t] = 1;
ans[idx++] = t + 1; //将答案放入ans数组 并把数组长度+1
}
for (int i = 0; i < idx; i++) {
for (int j = 0; j < idx - 1 - i; j++) {
if (ans[j] > ans[j + 1]) {
int temp = ans[j];
ans[j] = ans[j + 1];
ans[j + 1] = temp;
}
}
}
for(int i = 0 ; i < idx ; i ++) printf("%d ",ans[i]);
}
C This is an English question!
没想到要考英语吧哈哈哈
其实你就算读不懂直接把每个选项都打印一遍也可以(只是需要一些罚时)
D 短板
/*
* 本题核心思路:
* 容器的盛水量与底部长度和短板有关,只有底部长度和短板长度两者其中一个改变面积才会改变
* 所以可以定义一个对撞双指针,从数组的头部和尾部同时遍历整个数组, 并让双指针往中间收缩
* 在往中间收缩的过程中底部长度始终在减小
* 除去底部长度,盛水量只与短板长度有关,向内收缩的时候只需要指向短板的指针向内移动,长板指针不需要动
* 通过不断记录整个过程中 底部长度 * 短板长度 的较大值, 迭代得出容器盛水量的最大值
* (另外本题 O(n*n) 的时间复杂度会直接爆掉,不考虑暴力做法)
*/
#include <stdio.h>
#define N 100010
// 定一max函数,返回两个参数中的最大的那个值
int max(int a, int b)
{
if (a > b) return a;
return b;
}
int height[N]; ///< 定义一个 height 数组,大小为 100010
int main()
{
// 定义 n 表示 height 的长度,并初始化
int n;
scanf("%d", &n);
// 输入 height 数组的值
for (int i = 0; i < n; i ++ )
scanf("%d", &height[i]);
// ans表示最后的输出的答案,i 为左指针指向 height 数组最左侧的值height[0], j为右指针指向 height 最右侧的值 height[n - 1]
int ans = 0, i = 0, j = n - 1;
// 对撞双指针过程 核心代码
while (i < j) {
// 比较得出短板,然后进行记录并迭代
if (height[i] > height[j]) {// 当 height[i] == height[j] 时,i,j 谁为短板都不影响结果, 或者 此时认为i, j同时为短板也不影响
// 此时比较得 j 指向短板
ans = max(ans, (j - i) * height[j]);
-- j; // 短板更新,长板不动, j 向内移动, i 不变
}
else {
// 此时比较得 i 指向短板
ans = max(ans, (j - i) * height[i]);
++ i; // 短板更新, 长板不动 ,i 向内移动,j 不变
}
}
// 最后输出结果
printf("%d\n", ans);
return 0;
}
// ------------------------------------------- 我是一个分隔线 -----------------------------------------------------------
/*
当认为 height[i] == height[j] 时,i,j都为短板的迭代方式(max函数, 返回两个参数中较大的那一个,C语言没有内置max函数可以自己写一个)
核心代码:
while (i < j) {
if (height[i] > height[j]) {
ans = max(ans, (j - i) * height[j -- ]);
}
else if (height[i] == height[j]) {
ans = max(ans, (j - i) * height[j -- ]);
i ++ ;
}
else if (height[i] < height[j]) {
ans = max(ans, (j - i) * height[i ++ ]);
}
}
/--------------------------------- 我是一个分割线 ------------------------------------------------------------/
本题还有一种很简洁的写法, 利用三目运算符进行书写,只是看起来美观点和一开始第一种写法思路没什么区别
核心代码:
while (i < j) {
ans = height[i] > height[j] ?
max(ans, (j - i) * height[j -- ]):
max(ans, (j - i) * height[i ++ ]);
}
*/
E Simple Sequence——简单序列和
题解:利用二分和前缀和的思想。
这道题有一看 1 e 6 1e6 1e6的数据范围,那么肯定是不能暴力的。
我们需要求得的是 ∑ i = 1 n ∑ j = i n ∣ a i + a j − 10000 ∣ \sum_{i=1}^{n} \sum_{j=i}^{n}\lvert a_i+a_j-10000 \rvert ∑i=1n∑j=in∣ai+aj−10000∣的结果。
显然,我们输入的数据排序后与排序前得出的结果是一样的,那么我们不妨将输入的数据从小到大排序。同时我用 s u m sum sum数组作前缀和数组,记录前 i i i个的和,注意这个记录的是排序以后的。接着我用 v i m vim vim数组存储在我们输入的数据数组中第一个大于等于 10000 − n u m [ i ] 10000-num[i] 10000−num[i]的数的位置,那么在这个 v i m [ i ] vim[i] vim[i]以前的位置的数字加上 n u m [ i ] num[i] num[i]必然是小于 10000 10000 10000的,那么我们前面从 i ∼ v i m [ i ] − 1 i\sim vim[i]-1 i∼vim[i]−1的范围内的结果就是 ( 10000 − n u m [ i ] − n u m [ j ] ) (10000-num[i]-num[j]) (10000−num[i]−num[j])而在这之前总共会有 v i m [ i ] − i vim[i]-i vim[i]−i个类似的结果,也就是
∑ j = i v i m [ i ] ( 10000 − n u m [ i ] − n u m [ j ] ) \sum_{j=i}^{vim[i]}(10000-num[i]-num[j]) ∑j=ivim[i](10000−num[i]−num[j])
那么我可以将其转化为
10000 × ( v i m [ i ] − i ) − n u m [ i ] ∗ ( v i m [ i ] − i ) − ( s u m [ v i m [ i ] − 1 ] − s u m [ i − 1 ] ) 10000\times(vim[i]-i)-num[i]*(vim[i]-i)-(sum[vim[i]-1]-sum[i-1]) 10000×(vim[i]−i)−num[i]∗(vim[i]−i)−(sum[vim[i]−1]−sum[i−1])
而在 v i m [ i ] vim[i] vim[i]之后的即为 ∑ j = v i m [ i ] + 1 n ( n u m [ i ] + n u m [ j ] − 10000 ) \sum_{j=vim[i]+1}^{n}(num[i]+num[j]-10000) ∑j=vim[i]+1n(num[i]+num[j]−10000)。
将其转化为 s u m [ n ] − s u m [ v i m [ i ] − 1 ] + n u m [ i ] × ( n − v i m [ i ] + 1 ) − 10000 × ( n − v i m [ i ] + 1 ) sum[n]-sum[vim[i]-1]+num[i]\times(n-vim[i]+1)-10000\times(n-vim[i]+1) sum[n]−sum[vim[i]−1]+num[i]×(n−vim[i]+1)−10000×(n−vim[i]+1)。
但是我们要算绝对值内的值小于0的前提还有就是要 i ≥ v i m [ i ] i\geq vim[i] i≥vim[i],所以我们就会有如下核心代码
int pos = vim[i];
if(i < pos){
ans += (10000*(pos-i)-num[i]*(pos-i)-(sum[pos-1]-sum[i-1]));
ans += (sum[n]-sum[pos-1]+num[i]*(n-pos+1)-10000*(n-pos+1));
}
else
ans += (sum[n]-sum[i-1]+num[i]*(n-i+1)-10000*(n-i+1));
#include<bits/stdc++.h>
#include<algorithm>
#include<functional>
#include<vector>
#include<queue>
#define ll long long
#define pii pair<int,int>
#define inf 1e9
#define mem(s,i) memset(s,i,sizeof s)
using namespace std;
const int N = 1e6+10;
int t, n, m;
int num[N];
int sum[N];
int vim[N];
ll ans;
void solve(){
scanf("%d",&n);
for(int i = 1;i <= n;i++)scanf("%d",&num[i]);
sort(num+1,num+n+1);
for(int i = 1;i <= n;i++){
sum[i] = sum[i-1]+num[i];
vim[i] = lower_bound(num+1,num+n+1,10000-num[i])-(num);
// cout<<vim[i]<<" ";
}
for(int i = 1;i <= n;i++){
int pos = vim[i];
if(i < pos){
ans += (1ll*10000*(pos-i)-num[i]*(pos-i)-(sum[pos-1]-sum[i-1]));
ans += (sum[n]-sum[pos-1]+num[i]*(n-pos+1)-1ll*10000*(n-pos+1));
}
else
ans += (sum[n]-sum[i-1]+num[i]*(n-i+1)-1ll*10000*(n-i+1));
}
cout<<ans<<endl;
}
int main()
{
solve();
}
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6+10;
LL a[N];
LL sum1[N], sum2[N], res;
int index(LL q[], int n, LL x){
int l = 0, r = n;
while(l < r){
int mid = l+r >> 1;
if(q[mid+1] <= x) l = mid+1;
else r = mid;
}
return l;
}
int main()
{
int n; scanf("%d", &n);
for(int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
sort(a+1, a+n+1);
for(int i = 1; i <= n; i ++ ) sum1[i] = sum1[i-1]+fabs(a[i]-10000);
for(int i = n; i >= 1; i -- ) sum2[i] = sum2[i+1]+a[i]-10000;
for(int i = 1; i <= n; i ++ ){
int k = index(a, n, 10000-a[i]);
// cout << k << endl;
if(k >= i) res += sum1[k]-sum1[i-1]-(LL)(k-i+1)*a[i];
if(k >= n) continue;
if(i > k) k = i-1;
res += sum2[k+1] + (LL)(n-k)*a[i];
}
printf("%lld", res);
return 0;
}
F Simple Addition——简单加法
相当于26进制的运算
利用栈来模拟序列,方便从低位到高位的加法计算已经涉及到的进位。
参考代码中 f l a g flag flag就是表示进位的变量。
#include<bits/stdc++.h>
#include<algorithm>
#include<functional>
#include<vector>
#include<queue>
#define ll long long
#define pii pair<int,int>
#define inf 1e9
#define mem(s,i) memset(s,i,sizeof s)
using namespace std;
//字符——数字转换
int trans(char ch){
if(ch == '0')return 0;
else return (ch-'a'+1);
}
map<int,char> p;
//预处理
void init(){
p[0] = '0';
for(int i = 1;i <= 26;i++)p[i] = 'a' + i-1;
}
void solve(){
stack<char> s1, s2;
stack<char> ans;
string str1 = "", str2 = "";
cin >> str1 >> str2;
int l1 = str1.length(), l2 = str2.length();
if(l1 < l2){
string str = "";
str = str1;
str1 = str2;
str2 = str;
swap(l1, l2);
}
for(int i = 0;i < l1;i++){
s1.push(str1[i]);
}
for(int i = 0;i < l2;i++){
s2.push(str2[i]);
}
int flag = 0;
while(!s1.empty() && !s2.empty()){
char ch1 = s1.top();s1.pop();
char ch2 = s2.top();s2.pop();
int data1 = trans(ch1), data2 = trans(ch2);
int sum = data1 + data2 + flag;
int x = sum % 27;
int t = sum / 27;
flag = t;
ans.push(p[x]);
}
if(!s1.empty()){
while(!s1.empty()){
char ch1 = s1.top();s1.pop();
int data1 = trans(ch1);
int sum = data1 + flag;
int x = sum % 27;
int t = sum / 27;
flag = t;
ans.push(p[x]);
}
}
if(flag){
ans.push('a');
}
while(!ans.empty()){
char ch = ans.top();ans.pop();
cout << ch;
}
}
int main()
{
init();
solve();
}
//c语言版
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3+10;
char s[N];
int a[N], b[N], ans[N];
int main()
{
int lena, lenb;
scanf("%s", s);
lena = strlen(s);
for(int i = lena-1, j = 0; i >= 0; i --, j ++ ){
if(s[i] != '0') a[j] = s[i]-'a'+1;
else a[j] = 0;
}
scanf("%s", s);
lenb = strlen(s);
for(int i = lenb-1, j = 0; i >= 0; i --, j ++ ){
if(s[i] != '0') b[j] = s[i]-'a'+1;
else b[j] = 0;
}
int maxlen = max(lena, lenb);
int t = 0;
for(int i = 0; i < maxlen; i ++ ){
ans[i] = (a[i]+b[i]+t) % 27;
t = (a[i]+b[i]+t)/27;
}
ans[maxlen] = t;
if(ans[maxlen]) maxlen ++ ;
for(int i = maxlen-1; i >= 0; i -- ){
if(ans[i]) printf("%c", ans[i]+'a'-1);
else printf("0");
}
return 0;
}
I HH学长的名单
#include<stdio.h>
const int N = 1000000;
int n, m, ans;
int a[1000005];
int erFen(int q){ //整数二分
int l=1, r=n;
while (l < r){
int mid = l + r + 1 >> 1;
if (a[mid] <= q) l = mid; //如果 a[mid] <= q,那么在区间 [mid, r] 肯定还存在 q
else r = mid - 1; //如果 a[mid] > q,那么 q 只存在于区间 [l, mid-1]
}
if (a[l] == q) return l; //如果 a[l] == q,则找到了 q 最后一次出现的位置;反之,则序列中没有 q
else return 0;
}
int main(){
scanf("%d %d", &n, &m);
for (int i=1; i<=n; i++) scanf ("%d", &a[i]);
for (int i=1; i<=m; i++){
int q;
scanf("%d", &q);
ans = (ans + erFen(q) % N) % N; //防止 ans 超出 int 范围,所以一边求和,一边取余
}
printf("%06d", ans);
return 0;
}
J 水仙花数
#include<bits/stdc++.h>
using namespace std;
int imp(int x,int y){
int sum = 0,z = x;
while(x){
sum += pow(x%10,y);
x/=10;
}
if(sum == z) return 1;
return 0;
}
int main(){
int n;
cin >> n;
for(int i = pow(10,n-1);i<pow(10,n);i++){
if(imp(i,n)) cout << i << endl;
}
return 0;
}
K 我要子矩阵puls!
//学过时间复杂度的你一定发现了一维前缀和运行会超时吧 只能用二维前缀和
//再说一句 想通过这道题 告诉你们 补题很重要!!!
#include <stdio.h>
#define ll long long
ll sum[2005][2005];
int n,m,t;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ll x;
scanf("%lld",&x);
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x;
}
}
scanf("%d",&t);
int x1,x2,y1,y2;
while(t--){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%lld\n",sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]);
}
return 0;
}