数据结构线性表题解

1,大整数加法:

链接:http://xmnd.openjudge.cn/ds201602/1/

分析:题意很简单,给两个大整数,让你求出他们两的和

其实Java中就有专门用于大整数、大浮点数的运算,有兴趣的了解一下,用起来很方便

import java.math.BigInteger;
import java.util.Scanner;
public class Main {
 
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()){
			String str1 = sc.nextLine();
			String str2 = sc.nextLine();
			BigInteger x1 = new BigInteger(str1);
			BigInteger x2 = new BigInteger(str2);
			System.out.println(x1.add(x2));
		}
		sc.close();
	}
}

好了,下面来谈如何用c++解决这个问题

很显然,长度为100的大整数long long也存不下(long long范围2  ^ 64, 小于10 ^ 100),所以考虑转换成字符串来求解

说一下大体思路,比如:

11123

321

这两个数用字符串的形式来进行运算的过程如下:

先翻转两个字符串,方便从最低位开始操作:

32111

123

加起来:

44411

倒序输出:

11444

这个还是挺好理解的吧,那么现在总结一下操作步骤:

翻转两个字符串,从低位到高位相加,注意进位,再倒过来输出就行了

思想有了,接下来就是代码实现:

(忽略using namespace std及以上的所有代码,这些都是为了我平时写代码方便个人喜好加上去的)

#pragma GCC diagnostic error "-std=c++11"
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define all(x) x.begin(), x.end()
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

char a[205], b[205];//a, b数组保存的是输入
int x[205], y[205];//x, y数组保存的是字符串转换后的整数
//比如说a数组里面装的是123,实际上它们都是字符型的
//并不是所谓的一百二十三,要转换成整型才能进行相加得到正确的结果

int main()
{
	scanf("%s %s", a, b);//输入两行字符串
	int la = strlen(a);//strlen函数用于获取数组的长度,保存在la
	int lb = strlen(b);//同上
	int l = la > lb ? la : lb;//l保存的是两个数组中较长的一个的长度
	reverse(a, a + la);//因为懒得手写让数组翻转的函数,直接用了reverse函数
	//对于数组a, reverse(a + x, a + y)表示翻转区间[a[x], a[y-1]]
	//那么reverse(a, a + la)就是翻转[a[0], a[la-1]]区间,也就是整个a数组
	//因为数组下标从0开始嘛
	reverse(b, b + lb);//同上
	for(int i = 0; i < la; i++){//因为a,b里面保存的是字符,要转换成整数才能进行操作
		x[i] = a[i] - '0';//这一行的意思是:减去字符0的ACSII码
		//比如对于字符'7',我想获得它的整数值,就减去字符'0'就行了
		//也就是 (char)'7' - (char)'0' = (int)7
	}
	for(int i = 0; i < lb; i++){
		y[i] = b[i] - '0';
	}//同上

	//现在x, y数组里面装的就是整数表达了,开始进行相加操作
	for(int i = 0; i < l; i++){
		x[i] += y[i];//我准备把最后的结果直接保存在x数组里,就不需要另外开数组了
		if(x[i] >= 10){//进位操作,如果加起来大于10,当前数就减去10,它的高一位加1
			x[i+1]++;
			x[i] -= 10;
		}
	}
	while(x[l] == 0 && l >= 1){
		l--;//处理有前导0的情况,比如我们现在得到的x是12000
		//接下来倒序输出可不能输出00021,得把000去掉
	}
	for(int i = l; i >= 0; i--){//倒序输出
		printf("%d", x[i]);
	}
	printf("\n");//换个行
	return 0;
}

这是我这辈子写过的最详细的注释了QwQ...

2,约瑟夫问题

链接:http://xmnd.openjudge.cn/ds201602/2/

update:写了个简要解释:

关于这个做法的解释:

大致思路:每去掉一个人,都把当前位置的下一个人的编号设为0,得到一个新的约瑟夫环。后面的以此类推再来操作。

也就是每去掉一个人,问题的规模就减少一个人

那么很容易知道如何由旧约瑟夫环首元素下标(old)推出新下标(new)

new = (old – m) % n----------------------1式

(当然,这个结论的前提是首下标为0,所以这题最后有输出ans + 1操作)

那么:

 old = (new + m) % n;------------------------2式

现在我们就知道了如何由旧的推出新的,也就是如何从规模小的约瑟夫环的解推出大的约瑟夫环的解了

(其实1式已经能递归求解了,但考虑到数据较大可能爆栈,还是决定转换为2式,这样递推就能求解了)

 

Code:

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define FAST ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

int main()
{
	int n, m; scanf("%d %d", &n, &m);//输入
	int ans = 0;//答案保存在ans里面(ans代表answer)
	for(int i = 1; i <= n; i++){//因为每一轮出去一只猴子嘛,n轮下来就是答案(最后一只猴子的编号)了
		ans = (ans + m) % i;
	}
	printf("%d\n", ans + 1)
	return 0;
}

4:非递减有序集合合并

链接:http://xmnd.openjudge.cn/ds201602/4/

题目大意:给两个非递减的集合,求他们的并集,并且按非递减的顺序输出(并且每个元素具有集合元素的唯一性)

用STL里面的set即可完美解决,有兴趣自己了解:

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define FAST ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

int main()
{
	int n, m; cin >> n >> m;
	set<int> s;
	for(int i = 1; i <= n + m; i++){
		int x; cin >> x;
		s.insert(x);
	}
	cout << s.size() << endl;
	for(auto i : s){
		cout << i << ' ';
	}
	cout << endl;
	return 0;
}

接下来讲两个做法,一个O(n ^ 2)朴素做法,一个O(n)做法

由于数据范围较小,都可以过

第一种:直接将两个集合拼在一起,排个序,然后对于每一个元素,检查它是否与前一个相等,相等就不输出(为了满足唯一性)

比如一个已经合并好,排好序的集合

array: 1 2 2 3 4 5 5 5 7

vis    : 1 1 0 1 1 1 0 0 1

标为0就不输出,标为1就输出

那么最后的结果就是

1 2 3 4 5 7

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define FAST ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

int a[600];
int vis[600];//vis数组的作用是标记一个元素是不是与它的上一个元素相等

int main()
{
	int n, m; scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for(int i = n + 1; i <= n + m; i++) scanf("%d", &a[i]);//输入
	for(int i = 1; i <= n + m; i++){//冒泡排序
		for(int j = i; j <= n + m; j++){
			if(a[i] > a[j]){
				int t = a[i];
				a[i] = a[j];
				a[j] = t;
			}
		}
	}
	int cnt = 1;//计数输出集合的大小,初始化为1因为加上了第一个
	vis[1] = 1;//第一个是一定可以输出的
	for(int i = 2; i <= n + m; i++){
		if(a[i] == a[i-1]) vis[i] = 0;//相等就标为0
		else vis[i] = 1, cnt++;//不相等标为1,计数器加一
	}
	printf("%d\n", cnt);//先输出集合大小,题目要求的
	for(int i = 1; i <= n + m; i++){
		if(vis[i] == 1) printf("%d ", a[i]);//如果开始的标记为1,就说明这个元素的前一个和它不相等,就可以输出
	}
	printf("\n");//换个行
	return 0;
}

O(n)解法:

设置两个索引ia, ib,分别指向数组a, b的头元素,然后依次比较,谁的头元素比较小就放进答案数组,相等的话就不放,随便从两个选一个往后移一位,知道两个数组都遍历完为止

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define pb push_back
#define lowbit(x) x&(-x)
#define PII  pair<int, int> 
#define FAST ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)1e5 + 5;
using namespace std;

int a[300], b[300];
int ans[600];

int main()
{
	int n, m; scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for(int i = 1; i <= m; i++) scanf("%d", &b[i]);
	a[n+1] = b[m+1] = 1000000000;//两个数组的末尾应该初始化为很大的一个数,方便后面的比较大小操作
	int cnt = 0;//cnt记录答案数组的长度
	int ia = 1, ib = 1;//ia,ib分别指向数组a,b的头元素
	//输入的时候数组下标从1开始,所以ia,ib初始化为1
	while(ia != (n + 1) || ib != (m + 1)){//当ia,ib没有同时指到末尾,即都指到末尾了,结束了,就跳出循环
		if(a[ia] == b[ib]) ia++;//相等的话就不加,往后移一位(实际上这里是ib++也行)
		else if(a[ia] < b[ib]){//数组a的头比数组b的头小,就把a的头放进答案数组,并且cnt++, ia++
			ans[++cnt] = a[ia];
			ia++;
		}
		else if(a[ia] > b[ib]){//数组b的头比数组a的头小,就把b的头放进答案数组,并且cnt++, ib++
			ans[++cnt] = b[ib];
			ib++;
		}
	}
	printf("%d\n", cnt);//输出答案集合的大小
	for(int i = 1; i <= cnt; i++){//输出答案集合的元素
		printf("%d ", ans[i]);
	}
	printf("\n");//换个行
	return 0;
}

over

猜你喜欢

转载自blog.csdn.net/swunHJ/article/details/82869381
今日推荐