第十次训练赛
A - 小d的加法
Sample Input
22222222222222222222
33333333333333333333
Sample Output
55555555555555555555
思路:
知道是大数相加的模板题,但发现python真滴简单😂
#大数相加
n = int(input())
m = int(input())
print (n + m)
好了好了,正经写C++:
首先介绍一下什么是大数的存储方式
对于数字我们人类的习惯是先写高位再写低位,但是在计算机中计算的时候如果我们也按照从高位到低位存储数字,那么在运算的过程中会产生一些不必要的麻烦。比如对齐数位问题、进位问题都不是特别好处理,对于我们写程序造成了不必要的麻烦。所以在数组中我们按照从低位到高位存储大数,即现存个位,再存十位,再存百位……。比如我们有一个数组 A ,那么A[0]存个位,A[1]存十位,A[2]存百位……以此类推。
由于高精度数都比较大,所以在存储的过程中我们一般先以将数字存到一个 string 类型的字符串中,然后将其每一位减去 ‘0’ 再存入到数组中(因为在计算机中每一个字符都有它的ASCII码,字符’0’ - ‘9’ 的 ASCII码的范围为 48 - 57,用字符串中每一个字符的ASCII码减去 ‘0’ 的ASCII码正好对应相应的数字)。这里我们使用C++的 vector容器 来存储大数,方便增加以及输出数字。
高精度加法
其实高精度加法并没有我们想象中的那么难,实现的过程就是模拟了我们笔算加法的过程。下面是具体的讲解,如果觉得写的繁琐可以直接看代码
。首先从个位开始相加,先将两个数组的第一位A[0]、B[0]相加即两个数的个位相加。使用一个变量 t 来存储相加的结果,将想相加之后的结果对 10 取模然后存到结果数组的个位上即C[0]上。然后再使 t 除以 10 如果两个个位相加超过了 10 那么除以 10 之后的到的就是进位的结果,然后依次相加十位百位等等,直到两个数组的数字都加完。这里我们需要设置一个 变量 i = 0 来记录遍历数组的下标,如果 i 还没有超过数组最大的下标那么就相加,否则就不想加。
比如 1234 + 56 我们会将其存储为 4321 + 65,4 + 6 = 10 ,10 % 10 = 0,10 / 10 = 1,所以我们将 0 存储在C[0]上,t = 1,然后再将 t + 3 = 4 , t + 5 = 9,此时9 % 10 = 9,9 / 10 = 0 , t = 0,这时56已经加完了,所以我们直接计算 t + 2 = 2, t = 0,t + 1 = 1,t = 0。所以最终 C[0] = 0,C[1] = 9,C[2] = 2,C[3] = 1,倒着输出数组C中的数字,即 1290 就是我们的答案了。
==这里要特别注意:最后我们要判断一下 t 是否为 0 如果不为0 则说明最后还有进位需要在最后一位补 1 ,即在最高位补 1。
高精度加法模板
vector<int> add(vector<int> &A, vector<int> &B) {
vector<int> C;
int t = 0; // 进位
for (int i = 0; i < A.size() || i < B.size(); i++) {
if (i < A.size()) t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(1);
return C;
}
有了基本的了解,这个就很简单了。
#include<iostream>
#include<vector>
using namespace std;
vector<int> add(vector<int> &A, vector<int> &B) {
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || i < B.size(); i++) {
if (i < A.size())
t += A[i];
if (i < B.size())
t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t)
C.push_back(1);
return C;
}
int main() {
string a, b;
vector<int> A, B;
cin >> a >> b;
for (int i = a.size() - 1; i >= 0; i--)
A.push_back(a[i] - '0');
for (int i = b.size() - 1; i >= 0; i--)
B.push_back(b[i] - '0');
vector<int> C = add(A, B);
// 去除结果中的前导零
if (C.size() != 1) {
while (C.back() == 0) C.pop_back();
}
for (int i = C.size() - 1; i >= 0; i--)
printf("%d", C[i]);
return 0;
}
注意
:对于这个题目需要考虑前导零的情况,比如199 + 000001 和 0 + 0两种特殊情况
,对于第一种我们只需要判断一下最后返回的数组C是否有前导零,如果有直接弹出即可;第二种结果就是0,但是我们不能将其弹出否则就输出为空,那么会有一组测试样例无法通过。所以在去除前导零的时候需要判断一下C的长度如果是1那么证明答案为0或者为个位数我们就不需要弹出,所以代码中加了一个去除结果中的前导零的操作
B - 小d的小si树
Sample Input
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
Sample Output
3
?
思路:
简单的最小生成树问题,套下模板注意数据不足的点即可
什么是最小生成树:here
模板介绍:here
Prime算法AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int INF=0x3f3f3f3f;
int a[105][105];
int dis[105];
int n,m;
int Prime()
{
for(int i=1; i<=n; i++)
{
dis[i]=a[1][i];
}
dis[1]=0;
int ans=0;
for(int i=2;i<=n; i++)
{
int minn=INF;
int p=0;
for(int j=2; j<=n; j++)
{
if(dis[j]!=0&&dis[j]<minn)
minn=dis[p=j];
}
if(minn==INF)
return printf("?\n");
ans+=minn;
dis[p]=0;
for(int j=2; j<=n; j++)
{
if(dis[j]>a[p][j])
dis[j]=a[p][j];
}
}
return printf("%d\n",ans);
}
int main()
{
while(cin>>m>>n,m)
{
memset(a,INF,sizeof(a));
int x,y,z;
while(m--)
{
scanf("%d%d%d",&x,&y,&z);
a[x][y]=a[y][x]=z;
}
Prime();
}
return 0;
}
Kruskal算法AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int fa[105];
struct node
{
int s,e,w;
}a[5005];
int n,m;
bool cmp(node a,node b)
{
return a.w<b.w;
}
int Find(int x)
{
return x==fa[x]?x:x=Find(fa[x]);
}
int Kruskal()
{
int ans=0;
for(int i=1;i<=n;i++)
fa[i]=i;
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
int x=Find(a[i].s);
int y=Find(a[i].e);
if(x!=y)
{
ans+=a[i].w;
fa[x]=y;
}
}
return ans;
}
int main()
{
while(cin>>m>>n,m)
{
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].s,&a[i].e,&a[i].w);
}
int ans=Kruskal();
int cnt=0;
for(int i=1;i<=n;i++)
{
if(fa[i]==i)
cnt++;
}
if(cnt>1)
cout<<"?"<<endl;
else
cout<<ans<<endl;
}
return 0;
}
C - 小d和小y
思路:
好早以前在CF上做的 01游戏
每次要删除也只能删除一个0和一个1,并且很容易想到最后只会剩下全是0或者全是1,那么不管怎么操作,操作总数num都是固定不变为01串里0的个数和1的个数中的最小值,即min(num0, num1)
那么如果num为奇数那么先手赢,反之,后手赢
#include<bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
ll _, n, a[N];
void solve() {
string s; cin >> s;
int c1 = 0, c0 = 0;
for (int i = 0; i < s.length(); ++i)
if (s[i] == '0')c0++;
else c1++;
if (min(c0, c1) % 2)cout << "DA" << endl;
else cout << "NET" << endl;
}
int main() {
//freopen("in.txt", "r", stdin);
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> _; while (_--)solve();
}
D - 小d的一技能
题目大意:
一共有t组样例,每组给出两个数组,询问是否存在这两个数组中都有的序列,如果存在则打印YES,并找到长度最小的公共序列,打印长度并打印元素。不存在则打印NO。
解题思路:
大水题。标记一下A数组出现过哪些数,如果在B中找到了某一个则直接输出长度为1,并输出这个数,如果找不到则输出NO。AC代码:
#include<bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long ll;
const int N = 1100;
ll _, n, m, a[N], b[N], tag;
void solve() {
cin >> n >> m; tag = 0;
//if (n > m)swap(n, m);
for (int i = 0; i < n; ++i)cin >> a[i];
for (int i = 0; i < m; ++i) {
cin >> b[i];
for (int j = 0; j < n; ++j) {
if (a[j] == b[i])tag = a[j];
}
}
if (!tag)cout << "NO" << endl;
else {
cout << "YES" << endl;
cout << 1 << " " << tag << endl;
}
}
int main() {
//freopen("in.txt", "r", stdin);
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> _; while (_--)solve();
}
E - 小d的二技能
题目大意:
给你n个习惯的标准值,给你q个小习惯,问能保留的最多的习惯数和习惯数的标号。
思路:
一道搜索题,应该还是之前搜索的能力还不够,这道题我也做了好长时间,收获还是蛮大的,之前写dfs就完全是套用写全排列的模板,结果放到这题就超时了。
还是枚举所有的可能性,这种枚举方法在标记相同个数时还是按字典序排列的,具体看代码
#include <cstdio>
#include <cstring>
int a[25], b[20][25];
bool vis[20], r[20];
int n, m;
int ans;
bool ok()
{
for(int i=1; i<=n; i++)
{
int sum = 0;
for(int j=1; j<=m; j++)
sum += vis[j] * b[j][i];
if(sum < a[i])
return 0;
}
return 1;
}
void dfs(int cur)
{
if(cur == m+1)
{
if(ok())
{
int count = 0;
for(int i=1; i<=m; i++)
if(vis[i])
count++;
if(ans < count)
{
ans = count;
for(int i=1; i<=m; i++)
r[i] = vis[i];
}
}
return;
}
vis[cur] = 1;
dfs(cur + 1);
vis[cur] = 0;
dfs(cur + 1);
}
int main()
{
while(scanf("%d", &n) == 1)
{
memset(vis, 0, sizeof(vis));
memset(r, 0, sizeof(r));
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
scanf("%d", &m);
for(int i=1; i<=m; i++)
for(int j=1; j<=n; j++)
scanf("%d", &b[i][j]);
ans = 0;
dfs(1);
printf("%d", ans);
for(int i=1; i<=m; i++)
if(r[i])
printf(" %d", i);
printf("\n");
}
return 0;
}