蓝桥杯--每日一题2

素数

质数游戏2

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+10;
int prime[N];
int cnt;
bool vis[N];
int a[N];int n;
int ans;
//线性筛
void get_primes(int n)
{
  vis[0]=vis[1]=true;
  for(int i=2;i<=n;i++)
  {
    if(!vis[i]) prime[cnt++]=i;
    for(int j=0;prime[j]<=n/i;j++)
    {
      vis[prime[j]*i]=true;
      if(i%prime[j]==0) break;
    }
  }
}
void dfs(int x,int y,int s)
{
  if(x==n)
  {
    if(!vis[y]&&!vis[s]) ans++;
    return;
  }
  dfs(x+1,y,s);
  dfs(x+1,y+1,s+a[x]);
}
int main()
{
  get_primes(N-1);
  cin >> n;
  for(int i=0;i<n;i++) cin >> a[i];
  dfs(0,0,0);
  cout << ans;
  return 0;
}

魔法阵的能量

问题描述

在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
signed main()
{
    long long n;
    cin >> n;
    if(n & 1) return cout << 0 << '\n' , 0; // 如果 n 是奇数,末尾一定没有零
    long long ans = n / 10; // 先将 n 除以 10,统计其中的 2 的个数
    n /= 10;
    long long e = 5;
    while(e <= n){ // 统计其中的 5 的个数
        ans += n / e;
        e *= 5;
    }
    cout << ans << '\n';
    return 0;
}

阶乘分解

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const ll N = 80000004;
int prime[N];
int cnt;
bool vis[N];
ll n;
void get_prime()
{
  vis[0]=vis[1]=true;
  for(int i=2;i<=n;i++)
  {
    if(!vis[i]) prime[cnt++]=i;
    for(int j=0;prime[j]<=n/i;j++)
    {
      vis[prime[j]*i]=true;
      if(i%prime[j]==0) break;
    }
  }
}
int isprime(int k)
{
  int t=n,sum=0;
  while(t)
  {
    t/=k;
    sum+=t;
  }
  return sum;
}
int main()
{
  // 请在此输入您的代码
  cin >> n;
  get_prime();
  for(int i=0;i<cnt;i++)
  {
    cout << isprime(prime[i]) << endl;
  }
  return 0;
}

GCD和LCM

互质数的个数(过30的样例)

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
using ll =long long;
ll mod=998244353;
ll gcd(ll a,ll b){ return b?gcd(b,a%b):a;}
ll quick(ll a,ll b)
{
  ll ans=1;
  a%=mod;
  while(b)
  {
    if(b&1) ans=(ans*a)%mod;
    a=(a*a)%mod;
    b>>=1;
  }
  return ans;
}
int main()
{
  ll a,b;cin >> a >> b;
  ll mi=quick(a,b);
  ll ans=0;
  for(int i=1;i<mi;i++)
  {
    if(gcd(i,mi)==1) ans++;
  }
  cout << ans;
  return 0;
}

线性dp问题

2022

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll dp[2222][11][2222];
//do[i][j][k] 前i个数选择j个数,总体积为k的方案数
int main()
{
  //初始化
  for(int i=0;i<=2022;i++) dp[i][0][0]=1;

  for(int i=1;i<=2022;i++)
  {
    for(int j=1;j<=10;j++)
    {
      for(int k=1;k<=2022;k++)
      {
        //当前i比k还大,选不了
        if(k<i) dp[i][j][k]=dp[i-1][j][k];
        //i比k小,可以选,可以不选
        else dp[i][j][k]=dp[i-1][j][k]+dp[i-1][j-1][k-i];
      }
    }
  }
  cout << dp[2022][10][2022];
  return 0;
}

最长公共子序列

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll a[1005],b[1005];
ll dp[1005][1005];
//dp[i][j]:考虑前i-1个元素,前j-1个元素的最长公共子序列
int main()
{
  int n,m;cin >> n >> m;
  for(int i=1;i<=n;i++) cin >> a[i];
  for(int j=1;j<=m;j++) cin >> b[j];
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=m;j++)
    {
      //当相等时,将上一个最长的子序列+1
      if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1]+1;
      //不相等,从当前x或者y中选个最大的数
      else dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
    }
  }
  cout << dp[n][m];
  return 0;
}

最长上升子序列

问题描述

在这里插入图片描述

代码


//非ac代码
#include <iostream>
using namespace std;
const int N=3e5+6;
int a[N],dp[N];
int main()
{
  int n;cin >> n;
  for(int i=1;i<=n;i++) cin >> a[i];
  for(int i=1;i<=n;i++)
  {
    dp[i]=1;
    for(int j=1;j<i;j++)
    {
      if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1);
    }
  }
  int ans=0;
  for(int i=1;i<=n;i++)
  {
    ans=max(ans,dp[i]);
  }
  cout << ans;
  return 0;
}

//ac代码

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+9;
int a[N],low[N];
/*这里数据量太大,用普通lis会超时,
所以用二分加贪心的算法;
如果a[i]>当前的low[length],那么将它入到low数组中;
如果a[i]<=当前的low[length],找到第一个大于等于它的位置,去替换它;
最后length就是最长的长度;
*/
int main()
{
  int n;cin >> n;
  for(int i=1;i<=n;i++) cin >> a[i];
  low[1]=a[1];
  int length=1;
  for(int i=2;i<=n;i++)
  {
    if(a[i]>low[length]) low[++length]=a[i];
    else
    {
      int k=lower_bound(low+1,low+1+length,a[i])-low;
      low[k]=a[i];
    }
  }
  cout << length;
  return 0;
}

字符串转换

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
char a[3005],b[3005];
int dp[3005][3005];
//dp[i][j]表示a的前i个以及b的前j个字符修改成一样的需要的修改次数;
int main()
{
  scanf("%s %s",a+1,b+1);
  int n = strlen(a+1);
  int m = strlen(b+1);
  dp[0][0]=0;
  for(int i=1;i<=n;i++) dp[i][0]=i;
  for(int j=1;j<=m;j++) dp[0][j]=j;
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=m;j++)
    {
      if(a[i]==b[j]) dp[i][j]=dp[i-1][j-1];
      //删除一个字符:dp[i-1][j]+1;
      //插入一个字符:dp[i][j-1]+1;
      //将一个字符改为另一个字符:dp[i-1][j-1]+1;
      else
      {
        dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+1);
      }
    }
  }
  cout << dp[n][m];
  return 0;
}

过河卒

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll dp[25][25];//表示卒走到(i,j)这点的路径条数
bool s[25][25];
int main()
{
  // 请在此输入您的代码
  int bx,by,sx,sy;
  cin >> bx >> by >> sx >> sy;
  bx+=2,by+=2,sx+=2,sy+=2;
  s[sx][sy]=1;
  s[sx-1][sy+2]=1;
  s[sx-2][sy+1]=1;
  s[sx-2][sy-1]=1;
  s[sx-1][sy-2]=1;
  s[sx+1][sy-2]=1;
  s[sx+2][sy-1]=1;
  s[sx+2][sy+1]=1;
  s[sx+1][sy+2]=1;
  dp[2][1]=1;
  for(int i=2;i<=bx;i++)
  {
    for(int j=2;j<=by;j++)
    {
      if(s[i][j]) dp[i][j]=0;
      else dp[i][j]=dp[i-1][j]+dp[i][j-1];
    }
  }
  cout << dp[bx][by];
  return 0;
}

砝码称重

问题描述

在这里插入图片描述

代码

#include <iostream>
using namespace std;
using ll =long long;
bool dp[110][200000];
//表示前i个数相加或者相减,和为j的重量数
int main()
{
  // 请在此输入您的代码
  int n;cin >> n;
  dp[0][0]=1;
  for(int i=1;i<=n;i++)
  {
    int w;cin >> w;
    for(int j=0;j<=100000;j++)
    {
      //dp[i-1][j]:不用第i个数,和为j
      //dp[i-1][j-w]:用第i个数,做减法,等价于前i-1个数,实现j-w;
      //dp[i-1][j-w]:用第i个数,做加法,等价于前i-1个数,实现j+w;
      dp[i][j]=dp[i-1][j]|dp[i-1][abs(j-w)]|dp[i-1][j+w];
    }
  }
  ll ans=0;
  for(int i=1;i<=100000;i++) ans+=dp[n][i];
  cout << ans;
  return 0;
}

数字三角形

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
int dp[105][105];
//表示第i层第j个数的最大路径和
int a[105][105];
int main()
{
  int n;cin >> n;
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=i;j++)
    {
      cin >> a[i][j];
    }
  }

  dp[1][1]=a[1][1];

  for(int i=2;i<=n;i++)
  {
    for(int j=1;j<=i;j++)
    {
      dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j];
    }
  }
  if(n&1) cout << dp[n][n/2+1];
  else cout << max(dp[n][n/2],dp[n][n/2+1]);
  return 0;
}

子串简写

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
using ll =long long;
int main()
{
  int k;string s;char c1,c2;
  cin >> k >> s >> c1 >> c2;
  ll sum,sum1,ans;
  sum=sum1=ans=0;
  for(int i=k-1,j=0;i<s.size();i++,j++)
  {
    if(s[j]==c1) sum++;
    if(s[i]==c2) ans+=sum;
  }
  cout << ans;
  return 0;
}

接龙数列

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
int dp[100];
/*
 x 记录头数字,y 记录尾数字

 x 是前面某个字符串A的尾字符。此时肯定有贡献,可以和A一起接龙,最长序列长度加1。
由于前面可能有多个字符串的尾字符是x,为了形成最长接龙序列,
可以用dp[i]记录和更新以‘i’为尾字符的最长接龙长度。此时,第i个字符串的dp[y] = dp[x]+1。

 y 是前面某个字符串的尾字符,此时没有贡献,第i个字符的dp[y]可以赋值为前面以y字符为结尾的dp[y]。
*/
int main()
{
  int n;cin >> n;
  int ans=0;
  for(int i=1;i<=n;i++)
  {
    string s;cin >> s;
    int x=s[0]-'0';
    int y=s[s.size()-1]-'0';
    dp[y]=max(dp[y],dp[x]+1);
    ans=max(ans,dp[y]);
  }
  cout << n-ans;
  return 0;
}

最短路

蓝桥公园(floyd_Warshell,处理多源最短路)

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
// 使用标准命名空间,这样可以直接使用标准库中的函数和对象
using namespace std;
// 定义长整型别名 ll,方便后续使用
using ll = long long;
// 定义常量 N,表示图中节点的最大数量
const int N = 405;
// 定义常量 inf,表示无穷大,用于初始化距离矩阵
const ll inf = 2e18;
// 定义二维数组 d,用于存储任意两点之间的最短距离
ll d[N][N];


/*
这段代码实现了使用 Floyd-Warshall 算法来计算图中任意两点之间的最短路径,
并处理了多个查询,查询两点之间的最短距离。
如果两点之间不存在路径,则输出 -1。
*/

int main()
{
    // 读取输入的节点数量 n、边的数量 m 和查询数量 q
    int n,m,q;
    cin >> n >> m >> q;
    // 初始化距离矩阵 d,将任意两点之间的距离初始化为无穷大
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            d[i][j] = inf;

    // 任意节点到自身的距离为 0
    for(int i = 1; i <= n; i++)
        d[i][i] = 0;

    // 读取每条边的信息
    while(m--)
    {
        // 读取边的起点 u、终点 v 和边的权重 w
        ll u, v, w;
        cin >> u >> v >> w;
        // 更新距离矩阵 d,取当前存储的距离和新边权重的最小值
        d[u][v] = min(d[u][v], w);
        d[v][u] = min(d[v][u], w);
    }

    // 使用 Floyd-Warshall 算法计算任意两点之间的最短距离
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                // 更新距离矩阵 d,取当前存储的距离和经过中间节点 k 的距离的最小值
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

    // 处理查询
    while(q--)
    {
        // 读取查询的起点 st 和终点 ed
        int st, ed;
        cin >> st >> ed;
        // 如果起点到终点的距离仍为无穷大,说明两点之间不存在路径,输出 -1
        // 否则输出最短距离
        cout << (d[st][ed] == inf ? -1 : d[st][ed]) << endl;
    }
    return 0;
}

蓝桥王国(单源最短路,邻接表,dijk)

问题描述

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
// 定义长整型别名 ll,方便后续使用长整型数据
using ll = long long;
// 定义常量 N,作为数组的最大长度,用于存储图的节点信息
const ll N = 3e5 + 10;
// 定义常量 inf,代表无穷大,用于初始化距离
const ll inf = 2e18;

// 定义结构体 node,用于存储节点编号 u 和到该节点的距离 dis
struct node {
    ll u, dis;
    // 重载小于运算符,用于优先队列的比较
    // 这里将距离大的元素优先级设为低,实现小顶堆的效果
    bool operator<(const node& x) const {
        return dis > x.dis;
    }
};

// 数组 d 用于存储从起点到各个节点的最短距离
ll d[N];
// 邻接表 a,用于存储图的结构,a[u] 存储从节点 u 出发能到达的节点及其距离
vector<node> a[N];
// 图的节点数量 n 和边的数量 m
int n, m;
// 数组 vis 用于标记节点是否已经被访问过
bool vis[N];

// Dijkstra 算法函数,用于计算从起点 st 到各个节点的最短距离
void dijk(ll st) {
    // 初始化距离数组 d,将所有节点的距离设为无穷大
    for (int i = 1; i <= n; i++) {
        d[i] = inf;
    }
    // 初始化访问标记数组 vis,将所有节点标记为未访问
    memset(vis, false, sizeof vis);
    // 定义优先队列 pq,用于存储待处理的节点,优先处理距离小的节点
    priority_queue<node> pq;
    // 将起点 st 加入优先队列,起点到自身的距离为 0
    pq.push({st, d[st] = 0});
    // 当优先队列不为空时,继续处理
    while (pq.size()) {
        // 取出优先队列中距离最小的节点
        node tmp = pq.top();
        pq.pop();
        // 获取该节点的编号
        ll u = tmp.u;
        // 如果该节点已经被访问过,跳过
        if (vis[u]) continue;
        // 标记该节点为已访问
        vis[u] = true;
        // 遍历从节点 u 出发能到达的所有节点
        for (int i = 0; i < a[u].size(); i++) {
            // 获取能到达的节点编号
            ll y = a[u][i].u;
            // 获取从节点 u 到节点 y 的距离
            ll dy = a[u][i].dis;
            // 如果经过节点 u 到达节点 y 的距离更短
            if (d[u] + dy < d[y]) {
                // 更新节点 y 的最短距离
                d[y] = d[u] + dy;
                // 将更新后的节点 y 加入优先队列
                pq.push({y, d[y]});
            }
        }
    }
}

int main() {
    // 读取图的节点数量 n 和边的数量 m
    cin >> n >> m;
    // 循环读取每条边的信息
    while (m--) {
        // 读取边的起点 u、终点 v 和边的权重 w
        ll u, v, w;
        cin >> u >> v >> w;
        // 将边的信息加入邻接表 a 中
        a[u].push_back({v, w});
    }
    // 调用 Dijkstra 算法,计算从节点 1 到各个节点的最短距离
    dijk(1);
    // 输出从节点 1 到各个节点的最短距离
    for (int i = 1; i <= n; i++) {
        // 如果某个节点的距离仍为无穷大,说明无法到达,输出 -1
        // 否则输出最短距离
        cout << (d[i] >= inf ? -1 : d[i]) << " ";
    }
    return 0;
}

混境之地1

问题描述

在这里插入图片描述

输入

5 5
1 1 2 5
...#.
..#..
#...#
...#.
.....
2
1 2 5 3 1
1 3 1 5 2
7

输出

2

样例解释

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
// 定义 x 为 pair 的第一个元素,y 为 pair 的第二个元素,方便后续使用
#define x first
#define y second
// 定义 PII 为 pair<int, int> 类型,用于表示二维坐标
typedef pair<int,int> PII;
// 定义 PIII 为 pair<int, PII> 类型,用于优先队列中存储距离和坐标
typedef pair<int,PII> PIII;
// 定义常量 N 为 50,作为数组的最大大小
const int N = 50;
// n 表示地图的行数,m 表示地图的列数
int n,m;
// sx, sy 表示起点的坐标,ex, ey 表示终点的坐标
int sx,sy,ex,ey;
// k 表示传送门的数量,E 表示拥有的能量值
int k,E;
// p 数组用于存储每个传送门的消耗能量
int p[N][N];
// port 数组用于存储每个传送门的目标坐标
PII port[N][N];
// mp 数组用于存储地图信息
char mp[N][N];
// dist 数组用于存储从起点到每个点的最短距离
int dist[N][N];
// dx 和 dy 数组用于表示上下左右四个方向的偏移量
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
// vis 数组用于标记某个点是否已经被访问过
bool vis[N][N];

// 使用 Dijkstra 算法计算从起点到终点的最短路径
int dijkstra()
{
    // 定义一个优先队列,按照距离从小到大排序
    priority_queue<PIII, vector<PIII>,greater<PIII>> pq;
    // 将起点加入优先队列,初始距离为 0
    pq.push({0,{sx,sy}});
    // 将 dist 数组初始化为一个很大的值,表示无穷大
    memset(dist,0x3f,sizeof dist);
    // 起点到自身的距离为 0
    dist[sx][sy]=0;
    // 当优先队列不为空时,继续循环
    while(pq.size())
    {
        // 取出优先队列中距离最小的元素
        auto tt = pq.top();
        pq.pop();
        // 取出该元素的坐标
        auto t = tt.y;
        // 如果该点已经被访问过,跳过
        if(vis[t.x][t.y]) continue;
        // 标记该点为已访问
        vis[t.x][t.y]=true;
        // 遍历上下左右四个方向
        for(int i = 0; i < 4; i++)
        {
            // 计算下一个点的坐标
            int nx = t.x + dx[i], ny = t.y + dy[i];
            // 如果下一个点超出地图范围,跳过
            if(nx < 1 || nx > n || ny < 1 || ny > m) continue;
            // 如果下一个点是障碍物,跳过
            if(mp[nx][ny] == '#') continue;
            // 如果通过当前点到达下一个点的距离更短,更新距离
            if(dist[nx][ny] > dist[t.x][t.y] + 1)
            {
                dist[nx][ny] = dist[t.x][t.y] + 1;
                // 将更新后的点加入优先队列
                pq.push({dist[nx][ny],{nx,ny}});
            } 
        }
        // 如果当前点有传送门
        if(p[t.x][t.y])
        {
            // 取出传送门的目标坐标
            auto tt = port[t.x][t.y];
            int tx = tt.x, ty = tt.y;
            // 如果通过传送门到达目标点的距离更短,更新距离
            if(dist[tx][ty] > dist[t.x][t.y] + p[t.x][t.y])
            {
                dist[tx][ty] = dist[t.x][t.y] + p[t.x][t.y];
                // 将更新后的点加入优先队列
                pq.push({dist[tx][ty],{tx,ty}});
            }
        }
    }
    // 返回从起点到终点的最短距离
    return dist[ex][ey];
}

int main()
{
    // 输入地图的行数和列数
    cin >> n >> m;
    // 输入起点和终点的坐标
    cin >> sx >> sy >> ex >> ey;
    // 输入地图信息
    for(int i = 1; i <= n; i++) cin >> mp[i]+1;
    // 输入传送门的数量
    cin >> k;
    // 循环输入每个传送门的信息
    while(k--)
    {
        int a,b,c,d,pk;
        cin >> a >> b >> c >> d >> pk;
        // 记录传送门的起点和终点坐标
        port[a][b] = {c,d};
        // 记录传送门的消耗能量
        p[a][b] = pk;
    }
    // 输入拥有的能量值
    cin >> E;
    // 调用 Dijkstra 算法计算最短路径
    int res = dijkstra();
    // 如果能量值不足以到达终点,输出 -1
    if(E < res) cout << -1 << endl;
    // 否则输出剩余的能量值
    else cout << E - res << endl;
    return 0;
}

混镜之地3

问题描述

在这里插入图片描述

输入

5 5
1 1 5 5
...#.
..#..
#..CC
...#C
...#.
9

输出

Yes

输入输出样例解释

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
// 定义 x 为 pair 的第一个元素,y 为 pair 的第二个元素,方便后续使用
#define x first
#define y second
// 定义 PII 为 pair<int, int> 类型,用于表示二维坐标
typedef pair<int,int> PII;
// 定义 PIII 为 pair<int, PII> 类型,用于优先队列中存储距离和坐标
typedef pair<int,PII> PIII;
// n 表示地图的行数,m 表示地图的列数,E 表示拥有的能量
int n,m,E;
// sx, sy 表示起点的坐标,ex, ey 表示终点的坐标
int sx,sy,ex,ey;
// mp 数组用于存储地图信息
char mp[1005][1005];
// dist 数组用于存储从起点到每个点的最短距离
int dist[1005][1005];
// dx 和 dy 数组用于表示上下左右四个方向的偏移量
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
// vis 数组用于标记某个点是否已经被访问过
bool vis[1005][1005];

// 使用 Dijkstra 算法计算从起点到终点的最短路径
int dijkstra()
{
    // 定义一个优先队列,按照距离从小到大排序
    priority_queue<PIII,vector<PIII>,greater<PIII>> pq;
    // 将 dist 数组初始化为一个很大的值,表示无穷大
    memset(dist,0x3f,sizeof dist);
    // 将起点加入优先队列,初始距离为 0
    pq.push({0,{sx,sy}});
    // 起点到自身的距离为 0
    dist[sx][sy]=0;

    // 当优先队列不为空时,继续循环
    while(pq.size())
    {
        // 取出优先队列中距离最小的元素
        auto tt = pq.top();
        pq.pop();
        // 取出该元素的坐标
        auto t = tt.y;

        // 如果该点已经被访问过,跳过
        if(vis[t.x][t.y]) continue;
        // 标记该点为已访问
        vis[t.x][t.y]=true;

        // 遍历上下左右四个方向
        for(int i = 0; i < 4; i++)
        {
            // 计算下一个点的坐标
            int nx = t.x + dx[i], ny = t.y + dy[i];

            // 如果下一个点超出地图范围或者是障碍物,跳过
            if(nx <= 0 || nx > n || ny <= 0 || ny > m || mp[nx][ny] == '#') continue;

            // 计算移动到下一个点的代价
            // 如果是 '.' 则代价为 0,否则为字符对应的数值('A' 对应 1,'B' 对应 2,以此类推)
            int w = (mp[nx][ny] == '.' ? 0 : mp[nx][ny] - 'A' + 1);

            // 如果通过当前点到达下一个点的距离更短,更新距离
            if(dist[nx][ny] > dist[t.x][t.y] + w)
            {
                dist[nx][ny] = dist[t.x][t.y] + w;
                // 将更新后的点加入优先队列
                pq.push({dist[nx][ny],{nx,ny}});
            }
        }
    }
    // 返回从起点到终点的最短距离
    return dist[ex][ey];
}

int main()
{
    // 输入地图的行数和列数
    cin >> n >> m;
    // 输入起点和终点的坐标
    cin >> sx >> sy >> ex >> ey;
    // 输入地图信息
    for(int i = 1; i <= n; i++) cin >> mp[i]+1;
    // 输入拥有的能量
    cin >> E;

    // 调用 Dijkstra 算法计算最短路径
    int res = dijkstra();

    // 如果能量不足以到达终点,输出 "No"
    if(E < res) cout << "No" << endl;
    // 否则输出 "Yes"
    else cout << "Yes" << endl;

    return 0;
}

最短路问题

问题描述

在这里插入图片描述

输入

5 5
1 2
2 3
3 4
4 5
5 1

输出

2
-1
12
20
-1

代码

#include <bits/stdc++.h>
using namespace std;
// 使用 ll 作为 long long 类型的别名,方便后续使用
using ll = long long;
// 定义常量 N,用于表示数组的最大大小
const int N = 200 + 10;
// d 数组用于存储图中任意两点之间的最短距离
int d[N][N];
// n 表示图中节点的数量,q 表示查询的数量
int n, q;

// 计算两个数 a 和 b 的最大公约数
int gcd(int a, int b)
{
    // 如果 b 为 0,则 a 就是最大公约数,否则递归调用 gcd 函数
    return b == 0 ? a : gcd(b, a % b);
}

// 计算两个数 a 和 b 的最小公倍数
int lcm(int a, int b)
{
    // 根据公式 lcm(a, b) = a * b / gcd(a, b) 计算最小公倍数
    return a / gcd(a, b) * b;
}

// 判断一个数 n 是否为质数
bool isprime(int n)
{
    // 质数定义为大于 1 且只能被 1 和自身整除的数
    if (n < 2) return false;
    // 从 2 到 sqrt(n) 检查是否存在能整除 n 的数
    for (int i = 2; i <= sqrt(n); i++)
    {
        if (n % i == 0) return false;
    }
    return true;
}

// 使用 Floyd-Warshall 算法计算图中任意两点之间的最短路径
void floyd()
{
    // 中间节点 k 从 1 到 n 遍历
    for (int k = 1; k <= n; k++)
    {
        // 起点 i 从 1 到 n 遍历
        for (int i = 1; i <= n; i++)
        {
            // 终点 j 从 1 到 n 遍历
            for (int j = 1; j <= n; j++)
            {
                // 更新 d[i][j] 为 d[i][j] 和 d[i][k] + d[k][j] 中的较小值
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
}

int main()
{
    // 输入节点数量 n 和查询数量 q
    cin >> n >> q;
    // 将 d 数组初始化为一个很大的值,表示两点之间没有路径
    memset(d, 0x3f, sizeof d);
    // 初始化邻接矩阵
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            // 如果 i 和 j 中只有一个是质数
            if (isprime(i) + isprime(j) == 1)
            {
                // 则 i 到 j 的距离为它们的最小公倍数
                d[i][j] = lcm(i, j);
                // 由于是无向图,j 到 i 的距离也为它们的最小公倍数
                d[j][i] = lcm(i, j);
            }
        }
    }
    // 调用 Floyd-Warshall 算法计算任意两点之间的最短路径
    floyd();
    // 处理 q 次查询
    while (q--)
    {
        int x, y;
        // 输入查询的起点 x 和终点 y
        cin >> x >> y;
        // 如果 d[x][y] 仍然为初始的很大值,说明 x 到 y 没有路径
        if (d[x][y] == 0x3f3f3f3f) cout << -1 << endl;
        else cout << d[x][y] << endl;
    }
    return 0;
}

会面

问题描述

在这里插入图片描述

输入

4 4
1 2 1
2 3 2
3 4 3
4 1 1
1 4 2 3

输出

3

代码

#include <bits/stdc++.h>
using namespace std;
// 定义常量 N,用于表示数组的最大大小,这里 2e2 表示 200,再加 10 是为了防止越界
const int N = 2e2 + 10;
// n 表示图中的节点数量,m 表示图中的边数量
int n, m;
// d 数组用于存储图中任意两点之间的最短距离,是一个二维数组
int d[N][N];
// s1 和 e1 分别表示第一个人的起点和终点,s2 和 e2 分别表示第二个人的起点和终点
int s1, e1, s2, e2;

int main()
{
    // 输入图的节点数量 n 和边的数量 m
    cin >> n >> m;
    // 将 d 数组初始化为一个很大的值(0x3f 表示十六进制的 3f),用于表示两点之间没有路径
    memset(d, 0x3f, sizeof d);
    // 任何节点到自身的距离为 0
    for (int i = 1; i <= n; i++) d[i][i] = 0;

    // 循环 m 次,每次输入一条边的信息
    while (m--)
    {
        int a, b, t;
        // 输入边的两个端点 a 和 b,以及边的权重 t
        cin >> a >> b >> t;
        // 更新 a 到 b 的最短距离,如果当前边的权重更小,则更新
        d[a][b] = min(d[a][b], t);
        // 由于是无向图,b 到 a 的最短距离也更新
        d[b][a] = min(d[b][a], t);
    }

    // 使用 Floyd-Warshall 算法计算图中任意两点之间的最短路径
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                // 更新 d[i][j] 为 d[i][j] 和 d[i][k] + d[k][j] 中的较小值
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

    // 输入第一个人的起点 s1、终点 e1,以及第二个人的起点 s2、终点 e2
    cin >> s1 >> e1 >> s2 >> e2;

    // 初始化结果为一个很大的值,用于后续比较
    int res = 0x3f3f3f3f;
    // 枚举所有可能的中间节点 k
    for (int k = 1; k <= n; k++)
    {
        // 计算以 k 为中间节点时,两人从起点到 k 再到终点的最大距离
        // 取两人从起点到 k 的最大距离,加上两人从 k 到终点的最大距离
        res = min(res, max(d[s1][k], d[s2][k]) + max(d[k][e1], d[k][e2]));
    }
    // 如果结果仍然是初始的很大的值,说明没有可行的路径,输出 -1,否则输出结果
    cout << (res == 0x3f3f3f3f ? -1 : res) << endl;
    return 0;
}

慈善晚会

问题描述

在这里插入图片描述

输入

4 7 2
1 3 2
3 4 4
4 2 3
1 4 7
1 2 4
2 3 5
3 1 2

输出

12

代码

#include <bits/stdc++.h>
using namespace std;
// 定义常量 N,作为数组的最大长度,1e3 表示 1000,加 10 是为避免边界问题
const int N = 1e3 + 10;
// 定义 x 为 pair 的第一个元素,y 为 pair 的第二个元素,方便后续代码使用
#define x first 
#define y second
// 定义 PII 为 pair<int, int> 类型,用于存储距离和节点编号
typedef pair<int, int> PII;
// g 数组存储正向图的邻接表,h 数组存储反向图的邻接表
vector<PII> g[N], h[N];
// d1 数组存储从点 p 到其他点的最短距离,vis1 数组用于标记点是否在正向 Dijkstra 中被访问过
// d2 数组存储从其他点到点 p 的最短距离,vis2 数组用于标记点是否在反向 Dijkstra 中被访问过
int d1[N], vis1[N], d2[N], vis2[N];
// n 表示节点数量,m 表示边的数量,p 表示指定的节点
int n, m, p;

// 实现 Dijkstra 算法,计算最短路径
// d 数组用于存储最短距离,fl 用于区分是正向图还是反向图,vis 数组用于标记节点是否被访问过
void dijkstra(int d[], int fl, int vis[])
{
    // vec 数组用于存储当前使用的图(正向或反向)
    vector<PII> vec[N];
    for (int i = 1; i <= n; i++)
    {
        // 如果 fl 为 1,使用正向图 g
        if (fl == 1) vec[i] = g[i];
        // 否则使用反向图 h
        else vec[i] = h[i];
    }
    // 定义一个小顶堆优先队列 pq,用于存储待处理的节点及其距离
    priority_queue<PII, vector<PII>, greater<PII>> pq;
    // 初始化所有节点的最短距离为一个很大的值
    for (int i = 1; i <= n; i++) d[i] = 1061109567;
    // 点 p 到自身的距离为 0,并将其加入优先队列
    pq.push({d[p] = 0, p});
    // 循环处理优先队列中的节点
    while (pq.size())
    {
        // 取出距离最小的节点
        auto t = pq.top();
        pq.pop();
        // 如果该节点已经被访问过,跳过
        if (vis[t.y]) continue;
        // 标记该节点为已访问
        vis[t.y] = true;
        // 遍历该节点的所有邻接节点
        for (auto k : vec[t.y])
        {
            // 如果通过当前节点到达邻接节点的距离更短,更新最短距离
            if (d[k.y] > d[t.y] + k.x)
            {
                d[k.y] = d[t.y] + k.x;
                // 将更新后的邻接节点加入优先队列
                pq.push({d[k.y], k.y});
            }
        }
    }
}

int main()
{
    // 输入节点数量 n、边的数量 m 和指定节点 p
    cin >> n >> m >> p;
    // 循环读取每条边的信息
    while (m--)
    {
        int u, v, t;
        cin >> u >> v >> t;
        // 在正向图中添加边
        g[u].push_back({t, v});
        // 在反向图中添加边
        h[v].push_back({t, u});
    }
    // 调用 Dijkstra 算法计算从点 p 到其他点的最短距离
    dijkstra(d1, 1, vis1);
    // 调用 Dijkstra 算法计算从其他点到点 p 的最短距离
    dijkstra(d2, 0, vis2);
    // 用于存储最大的往返最短路径和
    int maxx = 0;
    // 遍历所有节点,计算最大的往返最短路径和
    for (int i = 1; i <= n; i++)
    {
        maxx = max(maxx, d1[i] + d2[i]);
    }
    // 输出最大的往返最短路径和
    cout << maxx << endl;
    return 0;
}

最小生成树

旅行销售员

问题描述

在这里插入图片描述

输入

2
6 7
1 2 3
2 3 3
3 1 5
3 4 4
4 5 4
4 6 3
6 5 5
3 3
1 2 1
2 3 2
3 1 3

输出

4
2

代码1(kruskal)

#include <bits/stdc++.h>
using namespace std;
// 使用 long long 类型的别名 ll
using ll = long long;

// 定义边的结构体,包含边的两个端点 x 和 y,以及边的权重 c
struct Edge
{
    ll x, y, c;
    // 重载小于运算符,用于边的排序,按照边的权重从小到大排序
    bool operator <(const Edge &u) const
    {
        return c < u.c;
    }
};

// 定义常量 N,作为数组的最大长度
const int N = 1e6 + 10;
// n 表示顶点数,m 表示边数
int n, m;
// fa 数组用于并查集,存储每个节点的父节点
int fa[N];

// 并查集的查找操作,用于查找节点 x 所在集合的代表元素
// 同时进行路径压缩,将节点 x 直接连接到集合的代表元素上
int find_set(int x)
{
    if (x == fa[x]) return x;
    else
    {
        fa[x] = find_set(fa[x]);
        return fa[x];
    }
}

int main()
{
    int t;
    // 输入测试用例的数量
    cin >> t;
    while (t--)
    {
        // 存储所有边的向量
        vector<Edge> es;
        // 输入当前测试用例的顶点数和边数
        cin >> n >> m;
        for (int i = 1; i <= m; i++)
        {
            ll x, y, c;
            // 输入每条边的两个端点和权重
            cin >> x >> y >> c;
            // 将边添加到向量中
            es.push_back({x, y, c});
        }
        // 对边按照权重从小到大进行排序
        sort(es.begin(), es.end());
        // 初始化并查集,每个节点的父节点是它自己
        for (int i = 1; i <= n; i++) fa[i] = i;
        // 用于存储最小生成树中最大边的权重
        ll ans = 0;
        // 遍历排序后的边
        for (auto k : es)
        {
            // 如果边的两个端点已经在同一个集合中,跳过该边
            if (find_set(k.x) == find_set(k.y)) continue;
            // 更新最大边的权重
            ans = max(ans, k.c);
            // 合并边的两个端点所在的集合
            fa[find_set(k.x)] = find_set(k.y);
        }
        // 输出最小生成树中最大边的权重
        cout << ans << endl;
    }
    return 0;
}

代码2(prim)

#include <bits/stdc++.h>
using namespace std;
// 使用 long long 类型的别名 ll
using ll = long long;

// 定义边的结构体,包含边的终点 u 和边的权重 c
struct Edge
{
    ll u, c;
    // 重载小于运算符,用于优先队列的比较,使得权重小的边优先级高
    bool operator <(const Edge &u) const
    {
        return c > u.c;
    }
};

// 定义常量 N 作为数组的最大长度,inf 作为无穷大的值
const ll N = 1e6 + 10, inf = 2e18;
// n 表示顶点数,m 表示边数
int n, m;
// g 数组用于存储图的邻接表,每个顶点对应一个存储边的向量
vector<Edge> g[N];
// vis 数组用于标记顶点是否已经被访问过
bool vis[N];
// d 数组用于存储每个顶点到最小生成树的最小距离
ll d[N];

// Prim 算法,用于求解最小生成树中最大边的权重
ll prim()
{
    // 初始化所有顶点为未访问状态
    memset(vis, false, sizeof vis);
    // 定义优先队列,存储边,按照边的权重从小到大排序
    priority_queue<Edge> pq;
    // 将顶点 1 加入优先队列,初始距离为 0
    pq.push({1, d[1] = 0});
    // 用于存储最小生成树中最大边的权重
    ll res = 0;
    // 当优先队列不为空时
    while (pq.size())
    {
        // 取出优先队列中权重最小的边的终点
        int x = pq.top().u;
        pq.pop();
        // 如果该顶点已经被访问过,跳过
        if (vis[x]) continue;
        // 标记该顶点为已访问
        vis[x] = true;
        // 更新最大边的权重
        res = max(res, d[x]);
        // 遍历该顶点的所有邻接边
        for (auto k : g[x])
        {
            // 邻接边的终点
            int y = k.u;
            // 邻接边的权重
            int w = k.c;
            // 如果通过当前边到达邻接顶点的距离更小
            if (w < d[y])
            {
                // 更新邻接顶点到最小生成树的最小距离
                pq.push({y, d[y] = w});
            }
        }
    }
    return res;
}

int main()
{
    int t;
    // 输入测试用例的数量
    cin >> t;
    while (t--)
    {
        // 输入当前测试用例的顶点数和边数
        cin >> n >> m;
        // 清空每个顶点的邻接表,初始化每个顶点到最小生成树的距离为无穷大
        for (int i = 1; i <= n; i++) g[i].clear(), d[i] = inf;
        for (int i = 1; i <= m; i++)
        {
            ll x, y, c;
            // 输入每条边的两个端点和权重
            cin >> x >> y >> c;
            // 无向图,将边添加到两个顶点的邻接表中
            g[x].push_back({y, c});
            g[y].push_back({x, c});
        }
        // 调用 Prim 算法求解最小生成树中最大边的权重并输出
        cout << prim() << endl;
    }
    return 0;
}

困难图

问题描述

在这里插入图片描述

输入

3 3
1 2 10
1 3 6
2 3 2

输出

14

代码

#include <bits/stdc++.h>
using namespace std;
// 定义长整型别名 ll
using ll = long long;

// 定义边的结构体,包含边的两个端点 x、y 以及边的权重 c
struct edge
{
    ll x, y, c;
    // 重载小于运算符,用于对边按权重从小到大排序
    bool operator <(const edge &e) const
    {
        return c < e.c;
    }
};

// 定义常量 N,作为数组的最大长度
const int N = 2e5+10;
// 存储所有边的向量
vector<edge> es;
// n 表示顶点数量,m 表示边的数量
int n, m;
// fa 数组用于并查集,存储每个顶点的父节点
// siz 数组用于记录每个连通分量的大小
ll fa[N], siz[N];

// 查找 x 所在集合的代表元素,使用路径压缩优化
int find_set(int x)
{
    // 如果 x 是自身的父节点,说明 x 是集合的代表元素
    if(x == fa[x]) return x;
    else
    {
        // 递归查找并更新 x 的父节点为集合的代表元素
        fa[x] = find_set(fa[x]);
        return fa[x];
    }
}

// 合并 x 和 y 所在的集合,使用按秩合并优化
void merge(int x, int y)
{
    // 找到 x 和 y 所在集合的代表元素
    int ra = find_set(x);
    int rb = find_set(y);
    // 保证将较小的集合合并到较大的集合中
    if(siz[ra] > siz[rb]) swap(ra, rb);
    // 将 ra 的父节点设为 rb
    fa[ra] = rb;
    // 更新 rb 所在集合的大小
    siz[rb] += siz[ra];
}

int main()
{
    // 输入顶点数量 n 和边的数量 m
    cin >> n >> m;
    // 循环读取每条边的信息
    for(int i = 1; i <= m; i++)
    {
        ll u, v, w;
        cin >> u >> v >> w;
        // 将边信息添加到边的向量中
        es.push_back({u, v, w});
    }

    // 初始化并查集,每个顶点的父节点是自身,集合大小为 1
    for(int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
    // 用于存储最终结果
    ll ans = 0;
    // 对边按权重从小到大排序
    sort(es.begin(), es.end());
    // 遍历排序后的边
    for(auto k : es)
    {
        // 如果边的两个端点已经在同一个集合中,跳过该边
        if(find_set(k.x) == find_set(k.y)) continue;
        // 计算该边对结果的贡献,即两个连通分量大小的乘积乘以边的权重
        ans += siz[find_set(k.x)] * siz[find_set(k.y)] * k.c;
        // 合并边的两个端点所在的集合
        merge(k.x, k.y);
    }
    // 输出最终结果
    cout << ans;
    return 0;
}

矿石建设

问题描述

在这里插入图片描述

输入

3 2
1 1
3 3
5 1
2 2
4 2

输出

8.49

样例解释

在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
// 定义宏,方便访问 pair 类型的元素
#define xx first
#define yy second

// 定义结构体 node,用于存储边的信息
// x 和 y 表示边的两个端点,w 表示边的权重
struct node
{
    int x, y;
    double w;
} g[10005];

// 定义两个数组,分别存储村庄和矿石点的坐标
// c 数组存储村庄的坐标,k 数组存储矿石点的坐标
pair<double, double> c[104];
pair<double, double> k[104];

// 并查集的父节点数组,用于判断两个节点是否属于同一个集合
int fa[104];

/*
总的思路就是先把村庄的最小权值找出来,在枚举矿石的点到村庄最近的距离
*/

// 并查集的查找函数,用于查找节点 x 所在集合的根节点
// 同时进行路径压缩,提高后续查找的效率
int root(int x)
{
    // 如果 x 是根节点,直接返回 x
    if (x == fa[x])
        return x;
    else
    {
        // 路径压缩,将 x 的父节点直接设为根节点
        fa[x] = root(fa[x]);
        return fa[x];
    }
}

// 比较函数,用于对边按照权重从小到大排序
bool cmp(node a, node b)
{
    return a.w < b.w;
}

int main()
{
    int n, m;
    // 输入村庄的数量 n 和矿石点的数量 m
    cin >> n >> m;

    // 输入每个村庄的坐标,并存储在 c 数组中
    for (int i = 1; i <= n; i++)
    {
        double x, y;
        cin >> x >> y;
        c[i] = {x, y};
    }

    // 输入每个矿石点的坐标,并存储在 k 数组中
    for (int i = 1; i <= m; i++)
    {
        double x, y;
        cin >> x >> y;
        k[i] = {x, y};
    }

    // 边的计数器,用于记录生成的边的数量
    int cnt = 1;

    // 枚举村庄之间的所有边,计算边的权重并存储在 g 数组中
    for (int i = 1; i <= n; i++)
    {
        for (int j = i + 1; j <= n; j++)
        {
            // 计算两点在 x 轴和 y 轴上的距离的平方
            double lx = pow(c[i].xx - c[j].xx, 2);
            double ly = pow(c[i].yy - c[j].yy, 2);
            // 计算两点之间的欧几里得距离
            double l = sqrt(lx + ly);
            // 将边的信息存储在 g 数组中
            g[cnt++] = {i, j, l};
        }
    }

    // 对边按照权重从小到大排序
    // 原代码此处有错误,正确的应该是 sort(g + 1, g + cnt, cmp);
    sort(g + 1, g + 1 + cnt, cmp);

    // 初始化并查集,每个节点的父节点初始化为自身
    for (int i = 1; i <= n; i++)
        fa[i] = i;

    // 用于存储最小生成树的总权重
    double ans = 0;

    // 遍历所有边,使用 Kruskal 算法构建最小生成树
    for (int i = 1; i <= cnt; i++)
    {
        // 如果边的两个端点已经在同一个集合中,跳过该边
        if (root(g[i].x) == root(g[i].y))
            continue;
        // 将该边的权重累加到最小生成树的总权重中
        ans += g[i].w;
        // 合并边的两个端点所在的集合
        fa[root(g[i].x)] = root(g[i].y);
    }

    // 枚举每个矿石点,找出其到所有村庄的最短距离
    for (int i = 1; i <= m; i++)
    {
        // 初始化最短距离为一个很大的值
        double mi = 1e17;
        for (int j = 1; j <= n; j++)
        {
            // 计算矿石点到村庄在 x 轴和 y 轴上的距离的平方
            double lx = pow(k[i].xx - c[j].xx, 2);
            double ly = pow(k[i].yy - c[j].yy, 2);
            // 计算矿石点到村庄的欧几里得距离
            double l = sqrt(lx + ly);
            // 更新最短距离
            mi = min(mi, l);
        }
        // 将最短距离累加到总权重中
        ans += mi;
    }

    // 输出最终的总权重,保留两位小数
    printf("%.2lf", ans);
    return 0;
}

道路加固

在这里插入图片描述

输入

5 6 1
1 2 5
1 3 4
2 3 3
2 4 2
3 5 1
4 5 6
3 5

输出

15
4

代码

#include <bits/stdc++.h>
using namespace std;

// 定义边的结构体
struct edge
{
    // 边的起点
    int x;
    // 边的终点
    int y;
    // 边的权重
    int c;
    // 重载小于运算符,用于对边按权重进行排序
    bool operator <(const edge &u)const
    {
        return c < u.c;
    }
};

// 并查集的父节点数组,用于判断两个点是否连通
int fa[10005];

// 查找元素 x 所在集合的根节点,并进行路径压缩
int root(int x)
{
    // 如果 x 是根节点,直接返回 x
    if(x==fa[x]) return x;
    else
    {
        // 递归查找根节点,并将 x 的父节点设为根节点
        fa[x]=root(fa[x]);
        return fa[x];
    }
}

// 存储所有边的向量
vector<edge>g;
// 二维数组,用于标记禁止使用的边
bool is[10005][10005];

int main()
{
    // n 表示顶点数量,m 表示边的数量,k 表示禁止使用的边的数量
    int n,m,k;
    cin >> n >> m >> k;

    // 读取所有边的信息
    for(int i=1;i<=m;i++)
    {
        // a 为起点,b 为终点,c 为边的权重
        int a,b,c;
        cin >> a >> b >> c;
        // 将边添加到向量 g 中
        g.push_back({a,b,c});
    }

    // 读取禁止使用的边的信息
    for(int i=1;i<=k;i++)
    {
        // x 和 y 为禁止边的两个端点
        int x,y;
        cin >> x >> y;
        // 标记该边为禁止使用
        is[x][y]=1;
        // 无向图,对称标记
        is[y][x]=1;
    }

    // 对所有边按权重从小到大进行排序
    sort(g.begin(),g.end());

    // 初始化并查集,每个顶点的父节点是其自身
    for(int i=1;i<=n;i++) fa[i]=i;

    // ans 用于记录最小生成树的总权重
    int ans=0;
    // cnt 用于记录最小生成树中边的数量
    int cnt=0;

    // 遍历所有边
    for(auto k:g)
    {
        // 如果该边是禁止使用的边,或者两个端点已经在同一个集合中,则跳过
        if(is[k.x][k.y]||root(k.x)==root(k.y)) continue;
        // 若边可用,则将边的权重累加到总权重中
        ans+=k.c;
        // 边的数量加 1
        cnt++;
        // 合并两个端点所在的集合
        fa[root(k.x)]=root(k.y);
    }

    // 输出最小生成树的总权重
    cout << ans << endl;
    // 输出最小生成树中边的数量
    cout << cnt;
    return 0;
}

高精度

加法

#include <bits/stdc++.h>
using namespace std;
string s,ss;
int a[10005],b[10005],c[10005];
int main()
{
	cin >> s;
	cin >> ss;
	 int l1=s.size();
	 int l2=ss.size();
	 
	 for(int i=0;i<l1;i++) a[l1-i]=s[i]-'0';
	 for(int i=0;i<l2;i++) b[l2-i]=ss[i]-'0';
	 int l=max(l1,l2);
	 
	
	 for(int i=1;i<=l;i++)
	 {
	 	c[i]+=a[i]+b[i];
	 	c[i+1]=c[i]/10;
	 	c[i]%=10;
		  
	 }
	 if(c[l+1]) l++;
	 for(int i=l;i>=1;i--) cout << c[i];
	return 0;
 } 

乘法

#include <bits/stdc++.h>
using namespace std;
int a[10005],b[10005],c[10005];
int main()
{
	string s;string ss;
	cin >> s >> ss ;
	int l1=s.size();
	int l2=ss.size();
	for(int i=1;i<=l1;i++) a[i]=s[l1-i]-'0';
	for(int i=1;i<=l2;i++) b[i]=ss[l2-i]-'0';
	
	for(int i=1;i<=l1;i++)
		for(int j=1;j<=l2;j++)
			c[i+j-1]+=a[i]*b[j];
	for(int i=1;i<l1+l2;i++)
	{
		if(c[i]>=10)
		{
			c[i+1]+=c[i]/10;
			c[i]%=10;
		}
	}
	int l=l1+l2;
	while(c[l]==0&&l>1) l--;
	for(int i=l;i>=1;i--) cout << c[i];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值