第十次训练赛

A - 小d的加法

计蒜客 - T1098

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树

HDU - 1863

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

CodeForces - 1373B

思路:

好早以前在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的一技能

CodeForces - 1382A

题目大意:

一共有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的二技能

HDU - 4152

题目大意:

给你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;
}
posted @ 2020-09-22 22:52  RioTian  阅读(138)  评论(1编辑  收藏  举报