ACM第二次考核题解

ACM第二次考核题解

难度题目编号题目考察知识点
简单CThis is an English question!
简单GACM在哪里?字符串
简单A2,4,8,16进制转换
简单J水仙花数水仙花数
一般FSimple Addition——简单加法高精度
一般H抓小偷快速幂
一般B聚餐排序
一般IHH学长的名单二分
一般D短板双指针
困难K我要子矩阵puls!二维前缀和
困难ESimple 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=1nj=inai+aj10000的结果。
​ 显然,我们输入的数据排序后与排序前得出的结果是一样的,那么我们不妨将输入的数据从小到大排序。同时我用 s u m sum sum数组作前缀和数组,记录前 i i i个的和,注意这个记录的是排序以后的。接着我用 v i m vim vim数组存储在我们输入的数据数组中第一个大于等于 10000 − n u m [ i ] 10000-num[i] 10000num[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 ivim[i]1的范围内的结果就是 ( 10000 − n u m [ i ] − n u m [ j ] ) (10000-num[i]-num[j]) (10000num[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](10000num[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[i1])
​ 而在 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]×(nvim[i]+1)10000×(nvim[i]+1)
​ 但是我们要算绝对值内的值小于0的前提还有就是要 i ≥ v i m [ i ] i\geq vim[i] ivim[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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值