1、快速排序
给定你一个长度为 n 的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n。
第二行包含 n个整数(所有整数均在 1∼1e9 范围内),表示整个数列。
输出格式
输出共一行,包含 n个整数,表示排好序的数列。
数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
模板代码:
#include<bits/stdc++.h>>
using namespace std;
const int N=1000000;
int q[N];
int n;
void quicksort(int q[],int l,int r)
{
if(l>=r)return;
int x=(q[r]+q[l])/2,i=l-1,k=r+1;
while(i<k)
{
do i++;while(q[i]<x);
do k--;while(q[k]>x);
if(i<k)swap(q[i],q[k]);
}
quicksort(q,l,k);
quicksort(q,k+1,r);
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
quicksort(q,0,n-1);
for(int i=0;i<n;i++) printf("%d ",q[i]);
return 0;
}
2、二分查找
\789. 数的范围
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 kk 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1
。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n个整数(均在 1∼10000 范围内),表示完整数组。
接下来 qq 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1
。
数据范围
1≤n≤100000 1≤q≤10000 1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int n,q,k;
int main()
{
cin>>n>>q;
for(int i=0;i<n;i++)cin>>a[i];
while(q--)
{
cin>>k;
int l=0,r=n-1;
while(l<r)
{
int mid=(l+r)/2;
if(a[mid]>=k)r=mid;
else l=mid+1;
}
if(a[l]!=k)cout<<"-1 -1"<<endl;
else
{
cout<<l<<" ";
int l=0;r=n-1;
while(l<r)
{
int mid=(l+r+1)/2;
if(a[mid]<=k)l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
return 0;
}
3、二分答案:
砍树
题目描述
伐木工人 Mirko 需要砍 M 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。
Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 H(米),伐木机升起一个巨大的锯片到高度 H,并锯掉所有树比 H高的部分(当然,树木不高于 HH 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20,15,10 和 17,Mirko 把锯片升到 15 米的高度,切割后树木剩下的高度将是 15,15,10 和 15,而 Mirko 将从第 1棵树得到 5 米,从第 4 棵树得到 2米,共得到 7米木材。
Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 H,使得他能得到的木材至少为 M米。换句话说,如果再升高 1 米,他将得不到 M 米木材。
输入格式
第 1 行 2个整数 N 和 M,N 表示树木的数量,M表示需要的木材总长度。
第 2行 N个整数表示每棵树的高度。
输出格式
1个整数,表示锯片的最高高度。
输入输出样例
输入 #1
4 7
20 15 10 17
输出 #1
15
输入 #2
5 20
4 42 40 26 46
输出 #2
36
说明/提示
对于 100\%100% 的测试数据,1<=N<=1e6,1<=M<=2e9,树的高度 <10^9,所有树的高度总和 >M。
代码
#include<bits/stdc++.h>
using namespace std;
long long n,bz,s=0,mid,leftt,longest,trees[1000008];
int main()
{
scanf("%lld%lld",&n,&bz);
for(int i=1;i<=n;i++)
{
scanf("%lld",&trees[i]);
longest=max(longest,trees[i]);//找到最长木材
}
while(leftt<=longest)
{
mid=(leftt+longest)/2; //从中间点开始作为伐木机高度
s=0;
for(int i=1;i<=n;i++)
if(trees[i]>mid) //树的高度大于伐木机高度
s+=trees[i]-mid; //高的部分累加
if(s<bz) //木材不足
longest=mid-1;//在左边搜 减小高度增加木材
else
leftt=mid+1;//在右边搜 增加高度减小木材
}
cout<<leftt-1;
return 0;
}
4、素数筛
筛质数
给定一个正整数 n,请你求出1∼n 中质数的个数。
输入格式
共一行,包含整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中质数的个数。
数据范围
1≤n≤1e6
输入样例:
8
输出样例:
4
模板代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
int ans=0;
for(int i=1;i<=n;i++)
{
int z=1;
for(int j=2;j<=sqrt(i);j++)
{
if(i%j==0)
{
z=0;
break;
}
}
if(z)ans++;
}
cout<<ans;
return 0;
}
5、整数数位分解
int a[10007],t=0;
void work(int x)
{
while(x)
{
a[t++]=x%10;
x/=10;
}
}
//x=1234,则a[]={4,3,2,1}
6、质因数分解
int a[10007],t=0;
void work(int x)
{
for(int i=1;i<=x;i++)
{
if(x%i==0)
{
a[t++]=i;
}
}
}
//15->a[]={1,3,5,15}
7、求gcd(最大公约数)&lcm(最小公倍数)
int gcd(int a,int b)
{
if(b==0)return a;
return gcd(b,a%b);
}
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
8、前缀和
前缀和
输入一个长度为 n 的整数序列。
接下来再输入 m个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。
输出格式
共 m 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n, 1≤n,m≤100000, −1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
模板代码:
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],s[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
while(m--)
{
int l,r;
cin>>l>>r;
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
9、差分
差分
输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r][l,r] 之间的每个数加上 cc。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。
输出格式
共一行,包含 n 个整数,表示最终序列。
数据范围
1≤n,m≤100000, 1≤l≤r≤n, −−1000≤c≤1000, −1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int arr[N],d[N];
int l,r,n,c,m;
void insert(int l,int r,int c)
{
d[l]+=c;
d[r+1]-=c;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>arr[i];
for(int i=1;i<=m;i++)
{
cin>>l>>r>>c;
insert(l,r,c);
}
for(int i=1;i<=n;i++)
{
d[i]+=d[i-1];
}
for(int i=1;i<=n;i++)
{
arr[i]+=d[i];
cout<<arr[i]<<" ";
}
return 0;
}
10、dfs全排列(n选m)
排列数字
给定一个整数 nn,将数字 1∼n1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 nn。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤71≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
难度:简单 |
时/空限制:1s / 64MB |
总通过数:77493 |
总尝试数:97846 |
来源:模板题 |
算法标签 |
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
bool st[N];
int s[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<n;i++)cout<<s[i]<<" ";
cout<<endl;
return;
}
for(int i=1;i<=n;i++)
{
if(!st[i])
{
s[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
}
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
11、01背包问题
01背包问题
有 N 件物品和一个容量是 V的背包。每件物品只能使用一次。
第 ii 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 NN 行,每行两个整数 vi,wi,用空格隔开,分别表示第 ii件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000 0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
状态转移方程:f[i]=max(f[i],f[i-v[i]]+w[i]);
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N];
int w[N],v[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
{
for(int j=m;j>=v[i];j--)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
12、完全背包问题:
完全背包问题
有 NN 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 ii 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000 0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10
状态转移方程:f[i]=max(f[ i ],f [ i-v[ i ] ]+w[ i ] )
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N];
int w[N],v[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
{
for(int j=v[i];j<=m;j++)//注意与01背包不同,j从小到大;
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
13、多重背包问题1
多重背包问题 I
有 N 种物品和一个容量是 V 的背包。
第 ii 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 NN 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100 0<vi,wi,si≤100
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N];
int w,v,s;
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>v>>w>>s;
for(int j=m;j>=v;j--)
{
for(int k=1;k<=s&&k*v<=j;k++)
{
f[j]=max(f[j],f[j-k*v]+k*w);
}
}
}
cout<<f[m]<<endl;
return 0;
}
多重背包问题II
多重背包问题 II
有 NN 种物品和一个容量是 V 的背包。
第 ii 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。 输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤1000 0<V≤2000 0<vi,wi,si≤2000
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int f[N];
int n,m;
struct Good
{
int v,w;
};
int main()
{
vector<Good> goods;
cin>>n>>m;
for(int i=0;i<n;i++)
{
int v,w,s;
cin>>v>>w>>s;
for(int k=1;k<=s;k*=2)
{
s-=k;
goods.push_back({v*k,w*k});
}
if(s>0)goods.push_back({v*s,w*s});
}
for(auto good:goods)
{
for(int j=m;j>=good.v;j--)
{
f[j]=max(f[j],f[j-good.v]+good.w);
}
}
cout<<f[m];
return 0;
}
14、Dijkstra最短路I
Dijkstra求最短路 I
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
输入格式
第一行包含整数 n和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
数据范围
1≤n≤500, 1≤m≤105, 图中涉及边长均不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=550;
int g[N][N];
int dist[N];
bool st[N];
int n,m;
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
}
st[t]=true;
for(int j=1;j<=n;j++)
{
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
if(dist[n]==0x3f3f3f3f)return -1;
return dist[n];
}
int main()
{
cin>>n>>m;
memset(g,0x3f,sizeof g);
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
}
cout<<dijkstra();
return 0;
}
Dijkstra求最短路 II
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。
数据范围
1≤n,m≤1.5×1e5, 图中涉及边长均不小于 00,且不超过 10000。 数据保证:如果最短路存在,则最短路的长度不超过 1e9。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
模板代码:
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 100010; // 把N改为150010就能ac
// 稀疏图用邻接表来存
int h[N], e[N], ne[N], idx;
int w[N]; // 用来存权重
int dist[N];
bool st[N]; // 如果为true说明这个点的最短路径已经确定
int n, m;
void add(int x, int y, int c)
{
// 有重边也不要紧,假设1->2有权重为2和3的边,再遍历到点1的时候2号点的距离会更新两次放入堆中
// 这样堆中会有很多冗余的点,但是在弹出的时候还是会弹出最小值2+x(x为之前确定的最短路径),
// 并标记st为true,所以下一次弹出3+x会continue不会向下执行。
w[idx] = c;
e[idx] = y;
ne[idx] = h[x];
h[x] = idx++;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof(dist));
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap; // 定义一个小根堆
// 这里heap中为什么要存pair呢,首先小根堆是根据距离来排的,所以有一个变量要是距离,
// 其次在从堆中拿出来的时候要知道知道这个点是哪个点,不然怎么更新邻接点呢?所以第二个变量要存点。
heap.push({ 0, 1 }); // 这个顺序不能倒,pair排序时是先根据first,再根据second,
// 这里显然要根据距离排序
while(heap.size())
{
PII k = heap.top(); // 取不在集合S中距离最短的点
heap.pop();
int ver = k.second, distance = k.first;
if(st[ver]) continue;
st[ver] = true;
for(int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i]; // i只是个下标,e中在存的是i这个下标对应的点。
if(dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({ dist[j], j });
}
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
else return dist[n];
}
int main()
{
memset(h, -1, sizeof(h));
scanf("%d%d", &n, &m);
while (m--)
{
int x, y, c;
scanf("%d%d%d", &x, &y, &c);
add(x, y, c);
}
cout << dijkstra() << endl;
return 0;
}
15、Floyd最短路
Floyd求最短路
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,边权可能为负数。
再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出 impossible
。
数据保证图中不存在负权回路。
输入格式
第一行包含三个整数 n,m,k。
接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
接下来 k 行,每行包含两个整数 x,y,表示询问点 x 到点 y 的最短距离。
输出格式
共 k 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible
。
数据范围
1≤n≤200, 1≤k≤n 1≤m≤20000, 图中涉及边长绝对值均不超过 10000。
输入样例:
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
输出样例:
impossible
1
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=210;
int d[N][N];
int n,m,q;
void floyd()
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
d[i][j]=min(d[i][j],d[k][j]);//d[i][j]表示从i到j的距离;
}
}
}
}
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)d[i][j]=0;
else d[i][j]=0x3f;
}
}
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
d[a][b]=min(d[a][b],c);
}
floyd();
while(q--)
{
int a,b;
cin>>a>>b;
if(d[a][b]==0x3f)cout<<"impossible"<<endl;
else cout<<d[a][b];
}
return 0;
}
16、高精度加法:
高精度加法
给定两个正整数(不含前导 0),计算它们的和。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的和。
数据范围
1≤整数长度≤100000
输入样例:
12
23
输出样例:
35
模板代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
string s,d;
int main()
{
cin>>s>>d;
int j=0;
for(int i=s.length()-1;i>=0;i--)
{
a[j++]=s[i]-'0';
}
j=0;
for(int i=d.length()-1;i>=0;i--)
{
b[j++]=d[i]-'0';
}
int n=max(s.length(),d.length());
for(int i=0;i<n;i++)
{
a[i]+=b[i];
a[i+1]+=a[i]/10;
a[i]%=10;
if(a[n]>0)n++;
}
for(int i=n-1;i>=0;i--)
{
cout<<a[i];
}
return 0;
}
17、高精度乘法
高精度乘法
给定两个非负整数(不含前导 0) A 和 B,请你计算 A×B 的值。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共一行,包含 A×B 的值。
数据范围
1≤A的长度≤100000 0≤B≤100000
输入样例:
2
3
输出样例:
6
模板代码:
#include <iostream>
#include <vector>
using namespace std;
vector<int> mul(vector<int> &A, vector<int> &B) {
vector<int> C(A.size() + B.size() + 7, 0); // 初始化为 0,C的size可以大一点
for (int i = 0; i < A.size(); i++)
for (int j = 0; j < B.size(); j++)
C[i + j] += A[i] * B[j];
int t = 0;
for (int i = 0; i < C.size(); i++) { // i = C.size() - 1时 t 一定小于 10
t += C[i];
C[i] = t % 10;
t /= 10;
}
while (C.size() > 1 && C.back() == 0) C.pop_back(); // 必须要去前导 0,因为最高位很可能是 0
return C;
}
int main() {
string a, b;
cin >> a >> b; // a = "1222323", b = "2323423423"
vector<int> 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');
auto C = mul(A, B);
for (int i = C.size() - 1; i >= 0; i--)
cout << C[i];
return 0;
}
18、BF字符串匹配
KMP字符串
给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P 在字符串 S 中多次作为子串出现。
求出模式串 P 在字符串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。
数据范围
1≤N≤1e5 1≤M≤1e6
输入样例:
3
aba
5
ababa
输出样例:
0 2
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
string s1,s2;
int main()
{
cin>>n;
cin>>s1;
cin>>m;
cin>>s2;
int x=0,num=0,t=0;
while(s2.find(s1,t)!=-1)
{
a[x]=s2.find(s1,t);
t=a[x]+1;
x++;
num++;
}
for(int i=0;i<num;i++)cout<<a[i]<<" ";
return 0;
}
19、Kruskal求最小生成树
Kruskal算法求最小生成树
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。
由 V 中的全部 n 个顶点和 EE 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
1≤n≤1e5, 1≤m≤2∗1e5, 图中涉及边的边权的绝对值均不超过 1000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
模板代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int p[N];//保存并查集
struct E{
int a;
int b;
int w;
bool operator < (const E& rhs){//通过边长进行排序
return this->w < rhs.w;
}
}edg[N * 2];
int res = 0;
int n, m;
int cnt = 0;
int find(int a){//并查集找祖宗
if(p[a] != a) p[a] = find(p[a]);
return p[a];
}
void klskr(){
for(int i = 1; i <= m; i++)//依次尝试加入每条边
{
int pa = find(edg[i].a);// a 点所在的集合
int pb = find(edg[i].b);// b 点所在的集合
if(pa != pb){//如果 a b 不在一个集合中
res += edg[i].w;//a b 之间这条边要
p[pa] = pb;// 合并a b
cnt ++; // 保留的边数量+1
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++) p[i] = i;//初始化并查集
for(int i = 1; i <= m; i++){//读入每条边
int a, b , c;
cin >> a >> b >>c;
edg[i] = {a, b, c};
}
sort(edg + 1, edg + m + 1);//按边长排序
klskr();
if(cnt < n - 1) {//如果保留的边小于点数-1,则不能连通
cout<< "impossible";
return 0;
}
cout << res;
return 0;
}
20、prim求最小生成树
Prim算法求最小生成树
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。
由 V 中的全部 n 个顶点和 EE 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
1≤n≤500, 1≤m≤1e5, 图中涉及边的边权的绝对值均不超过 10000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
模板代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510;
int g[N][N];//存储图
int dt[N];//存储各个节点到生成树的距离
int st[N];//节点是否被加入到生成树中
int pre[N];//节点的前去节点
int n, m;//n 个节点,m 条边
void prim()
{
memset(dt,0x3f, sizeof(dt));//初始化距离数组为一个很大的数(10亿左右)
int res= 0;
dt[1] = 0;//从 1 号节点开始生成
for(int i = 0; i < n; i++)//每次循环选出一个点加入到生成树
{
int t = -1;
for(int j = 1; j <= n; j++)//每个节点一次判断
{
if(!st[j] && (t == -1 || dt[j] < dt[t]))//如果没有在树中,且到树的距离最短,则选择该点
t = j;
}
//2022.6.1 发现测试用例加强后,需要判断孤立点了
//如果孤立点,直返输出不能,然后退出
if(dt[t] == 0x3f3f3f3f) {
cout << "impossible";
return;
}
st[t] = 1;// 选择该点
res += dt[t];
for(int i = 1; i <= n; i++)//更新生成树外的点到生成树的距离
{
if(dt[i] > g[t][i] && !st[i])//从 t 到节点 i 的距离小于原来距离,则更新。
{
dt[i] = g[t][i];//更新距离
pre[i] = t;//从 t 到 i 的距离更短,i 的前驱变为 t.
}
}
}
cout << res;
}
void getPath()//输出各个边
{
for(int i = n; i > 1; i--)//n 个节点,所以有 n-1 条边。
{
cout << i <<" " << pre[i] << " "<< endl;// i 是节点编号,pre[i] 是 i 节点的前驱节点。他们构成一条边。
}
}
int main()
{
memset(g, 0x3f, sizeof(g));//各个点之间的距离初始化成很大的数
cin >> n >> m;//输入节点数和边数
while(m --)
{
int a, b, w;
cin >> a >> b >> w;//输出边的两个顶点和权重
g[a][b] = g[b][a] = min(g[a][b],w);//存储权重
}
prim();//求最下生成树
//getPath();//输出路径
return 0;
}