今日总结2024/5/31

今日熟悉了常用库函数,并查集,常用建边方式

明天蓝桥杯国赛随缘了,第一次估计也是最后一次了

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->t

struct 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值