今日熟悉了常用库函数,并查集,常用建边方式
明天蓝桥杯国赛随缘了,第一次估计也是最后一次了
LQOJ.269 排列序数
如果用 a b c d 这 4 个字母组成一个串,有 4!=24 种,如果把它们排个序,每个串都对应一个序号:
abcd 0
abdc 1
acbd 2
acdb 3
adbc 4
adcb 5
bacd 6
⋯⋯
现在有不多于 10 个两两不同的小写字母,给出它们组成的串,你能求出该串在所有排列中的序号吗?
next_permutation只能获得下一个排列,如果要获得全排列,那么就需要先对数组进行升序排序
调用next_permutation函数即可
#include <iostream>
using namespace std;
#include <algorithm>
int main()
{string s;cin>>s;string s2(s);sort(s2.begin(),s2.end());int idx=0;do{if(s2==s){cout<<idx;break;}idx++;}while(next_permutation(s2.begin(),s2.end()));return 0;
}
std::count函数,统计迭代器begin和end中 一个元素出现的次数
LQOJ 227. 交换次数
招聘部门一字排开。由于是自由抢占席位,三大公司的席位随机交错在一起,形如:BABTATT,这使得应聘者十分别扭。
于是,管理部门要求招聘方进行必要的交换位置,使得每个集团的席位都挨在一起。即最后形如:BBAAATTT 这样的形状,当然,也可能是:AAABBTTT 等。
现在,假设每次只能交换 2 个席位,并且知道现在的席位分布,你的任务是计算:要使每个集团的招聘席位都挨在一起需要至少进行多少次交换动作。
输入描述
输入是一行 n 个字符(只含有字母 B、A 或 T ),表示现在的席位分布。
输出描述
输出是一个整数,表示至少交换次数。
#include <bits/stdc++.h>
using namespace std;
int ans=0x3f3f3f3f;
string s;int solve(string c){//假设刚开始是ABT
int a1=count(s.begin(),s.end(),c[0]);
int a2=count(s.begin(),s.end(),c[2]);
int l1=count(s.begin(),s.begin()+a1,c[1]);//求出把A中的B交换的次数
int l2=count(s.begin(),s.begin()+a1,c[2]);//求出把A中T交换的次数
int l3=count(s.end()-a2,s.end(),c[0]);
int l4=count(s.end()-a2,s.end(),c[1]);
return l1+l2+l3+l4-min(l2,l3);//减去重复计算的
}int main(){cin>>s;string s1="ABT";do{ans=min(ans,solve(s1));//全部情况取最小值}while(next_permutation(s1.begin(),s1.end()));cout<<ans;return 0;
}
暴力枚举所有情况选最小即可
[ABC079D] Wall
你面前有一堵墙,墙上有数字,你需要将墙上的数字都变成 1
。
现在给出一个 W×H 的矩阵 A 表示墙上数字的情况。
其中若 Ai,j=−1 ,则表示位置 (i,j) 上没有数字,否则 Ai,j 的值表示墙上(i,j) 位置的数字。
当然,你还有一张 10×10 的表 C,其中 Ci,j 表示把数字 i 转化成数字 j 所需要的花费。
求花费的最小值。
通过费用表可以建立每个数字进行相互转化的带权边,且每个数字可以通过间接转化来达到最小花费,因此可以使用floyd
然后遍历墙上每一个数累加最小花费即可
#include <bits/stdc++.h>
using namespace std;
int h,w;
const int N=11,M=210;
int c[N][N],g[M][M];void floyd(){for(int k=0;k<=9;k++)for(int i=0;i<=9;i++)for(int j=0;j<=9;j++){c[i][j]=min(c[i][j],c[i][k]+c[k][j]);}
}int main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);cin>>h>>w;for(int i=0;i<=9;i++)for(int j=0;j<=9;j++)cin>>c[i][j];floyd();//把转换费用当作带权边int ans=0;for(int i=1;i<=h;i++)for(int j=1;j<=w;j++){cin>>g[i][j];if(g[i][j]==-1||g[i][j]==1) continue;ans+=c[g[i][j]][1];}cout<<ans;return 0;
}
P1196 [NOI2002] 银河英雄传说
合并指令为 M i j
,含义为第 i 号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第 j 号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。
然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通过庞大的情报网络随时监听杨威利的舰队调动指令。
在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战舰分布情况,也会发出一些询问指令:C i j
。该指令意思是,询问电脑,杨威利的第 i 号战舰与第 j 号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。
作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以及回答莱因哈特的询问。
可以很容易想到用并查集来模拟合并操作,但是要多维护一个战舰之间的距离,因此使用带权并查集来进行合并
#include <bits/stdc++.h>
using namespace std;
const int N=3e4+5;
int d[N],size[N],p[N];int find(int u){if(p[u]!=u){int root=find(p[u]);d[u]+=d[p[u]];//递归加距离p[u]=root;}return p[u];
}int main(){int t;cin>>t;for(int i=0;i<=N-1;i++){size[i]=1;//初始化并查集p[i]=i;//距离刚开始都默认为0}while(t--){char op;int i,j;cin>>op>>i>>j;if(op=='M'){int pa=find(i),pb=find(j);d[pa]=size[pb];//被接入的根结点初始化距离为到b根的距离size[pb]+=size[pa];p[pa]=pb;}else{int pa=find(i),pb=find(j);if(pa!=pb) cout<<-1<<'\n';else cout<<max(0,abs(d[i]-d[j])-1)<<'\n';//记住是对于战舰i和战舰j之间的距离}}return 0;
}
P1656 炸铁路
A 国派出将军 uim,对 B 国进行战略性措施,以解救涂炭的生灵。
B 国有 n 个城市,这些城市以铁路相连。任意两个城市都可以通过铁路直接或者间接到达。
uim 发现有些铁路被毁坏之后,某两个城市无法互相通过铁路到达。这样的铁路就被称为 key road。
uim 为了尽快使该国的物流系统瘫痪,希望炸毁铁路,以达到存在某两个城市无法互相通过铁路到达的效果。
然而,只有一发炮弹(A 国国会不给钱了)。所以,他能轰炸哪一条铁路呢?
首先把每一条铁路存起来,然后考虑连通性,枚举每一根铁路然后根据次数除这条铁路外的这两点如果不联通,则说明这条路是这两点唯一的路,输出即可,然后必须对铁路进行排序再枚举,才能按字典序输出
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N=160,M=5010;
PII t[M];
int n,m,cnt,p[N];int find(int u){if(p[u]!=u) p[u]=find(p[u]);return p[u];
}int main(){cin>>n>>m;for(int i=0;i<m;i++){int a,b;cin>>a>>b;if(a>b) swap(a,b);t[cnt++]={a,b};}sort(t,t+cnt);//因为枚举的边要从小到大for(int i=0;i<cnt;i++){for(int j=1;j<=n;j++) p[j]=j;//初始化for(int j=0;j<cnt;j++){if(i==j) continue;//当前处理这条不做处理int pa=find(t[j].x),pb=find(t[j].y);if(pa!=pb)p[pa]=pb;}//开始枚举每条边if(find(t[i].x)!=find(t[i].y)) cout<<t[i].x<<' '<<t[i].y<<'\n';}return 0;
}
P1396 营救
妈妈下班回家,街坊邻居说小明被一群陌生人强行押上了警车!妈妈丰富的经验告诉她小明被带到了 t 区,而自己在 s 区。
该市有 m 条大道连接 n 个区,一条大道将两个区相连接,每个大道有一个拥挤度。小明的妈妈虽然很着急,但是不愿意拥挤的人潮冲乱了她优雅的步伐。所以请你帮她规划一条从 s 至 t 的路线,使得经过道路的拥挤度最大值最小。
使用并查集维护最小生成树,当s->t第一次联通时的最大拥挤度,就是答案
Kruskal最小生成树-重载函数,边排序,每次选最小的进行联通
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=2e4+5;
int p[N],n,m,s,t;//n个区,m条边,s->tstruct edge{int u,v,w;bool operator<(edge &W){return w<W.w;//从小到大排}
}Edge[M];int find(int u){if(p[u]!=u) p[u]=find(p[u]);return p[u];
}int main(){cin>>n>>m>>s>>t;for(int i=0;i<m;i++){cin>>Edge[i].u>>Edge[i].v>>Edge[i].w;}sort(Edge,Edge+m);//按权值排序for(int i=1;i<=n;i++) p[i]=i;int res=0;for(int i=0;i<m;i++){int a=find(Edge[i].u),b=find(Edge[i].v),c=Edge[i].w;if(a!=b){p[a]=b;res=max(res,c);}if(find(s)==find(t)){cout<<res;break;}}return 0;
}