今日总结2024/5/24

今日学习了最短编辑距离和并查集相关应用

P2758 编辑距离

设 𝐴和 𝐵是两个字符串。我们要用最少的字符操作次数,将字符串 𝐴转换为字符串 𝐵。这里所说的字符操作共有三种:

  1. 删除一个字符;
  2. 插入一个字符;
  3. 将一个字符改为另一个字符。

A,B 均只包含小写字母。

输入格式

第一行为字符串 A;第二行为字符串 B;字符串 A,B 的长度均小于 2000。

输出格式

只有一个正整数,为最少字符操作次数。

根据最后一次的状态进行划分即可,可以分为a的前i-1已经和b的前j个相等,或者a的前i已经和b的前j-1个相等,或者长度相同时看最后一位的状态来进行划分

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=5e3+10;
int f[N][N];//把a的前i个,b的前j个修改为相同字符串的最小操作次数
/*对a做修改-看最后一次操作
f[i-1][j]+1 删除第i个字符后与b相等
f[i][j-1]+1 插入一个字符后和b相等
f[i-1][j-1]+1 把最后一个字符改成b
*/
int main(){
	string s1,s2;
	cin>>s1>>s2;
	memset(f,0x3f,sizeof f);
	for(int i=0;i<=s2.size();i++) f[0][i]=i;//s1为空只能添加
	for(int i=0;i<=s1.size();i++) f[i][0]=i;//s2为空,只能删除
	//都为空就操作0次初始化为0
	
	for(int i=1;i<=s1.size();i++)
	for(int j=1;j<=s2.size();j++){
		f[i][j]=min(f[i][j-1],f[i-1][j])+1;
		if(s1[i-1]==s2[j-1]) f[i][j]=min(f[i][j],f[i-1][j-1]);
		else f[i][j]=min(f[i][j],f[i-1][j-1]+1);
	}
	cout<<f[s1.size()][s2.size()];
	return 0;
}
AcWing 1250. 格子游戏

Alice和Bob玩了一个古老的游戏:首先画一个 n×n的点阵(下图 n=3)。

接着,他们两个轮流在相邻的点之间画上红边和蓝边:

直到围成一个封闭的圈(面积不必为 1)为止,"封圈"的那个人就是赢家。因为棋盘实在是太大了,他们的游戏实在是太长了!

他们甚至在游戏中都不知道谁赢得了游戏。

于是请你写一个程序,帮助他们计算他们是否结束了游戏?

输入格式

输入数据第一行为两个整数 n 和 m。n表示点阵的大小,m 表示一共画了 m条线。

以后 m行,每行首先有两个数字 (x,y),代表了画线的起点坐标,接着用空格隔开一个字符,假如字符是 D,则是向下连一条边,如果是 R就是向右连一条边。

输入数据不会有重复的边且保证正确。

输出格式

输出一行:在第几步的时候结束。

假如 m步之后也没有结束,则输出一行"draw"。

数据范围

1≤n≤200

1≤m≤24000
输入样例:

cpp 复制代码
3 5
1 1 D
1 1 R
1 2 D
2 1 R
2 2 D

输出样例:

4

可以把每个点看出一个集合,添加一条边就是合并两个集合,当合并的两个点也就是一条边在同一个集合内的时候,则出现了一个回路也就结束了

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N=40010;
int p[N],n,m;

int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}

int get(int x,int y){
	return x*(n-1)+y;//将二维坐标转换成1维开始编号
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) p[i]=i;//初始化并查集
	int ans=0;//来记录第一次出现环的地方
	bool tag=false;
	for(int i=1;i<=m;i++){
		int x,y;//存起点
		char op;
		cin>>x>>y>>op;
		int a=get(x,y),b;//来存要画的边的目标点
		if(op=='D') b=get(x+1,y);
		else b=get(x,y+1);
		if(find(a)==find(b)&&!tag){
			ans=i;//记录第一次答案
			tag=true;
		}
		p[b]=find(a);//把新加入的点b放入集合内
	}
	if(ans) cout<<ans;
	else cout<<"draw";
	return 0;
}
AcWing 1252. 搭配购买

Joe觉得云朵很美,决定去山上的商店买一些云朵。

商店里有 n 朵云,云朵被编号为 1,2,...,n ,并且每朵云都有一个价值。

但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。

但是Joe的钱有限,所以他希望买的价值越多越好。

输入格式

第 1 行包含三个整数 n,m,w ,表示有 n 朵云, m 个搭配,Joe有 w 的钱。

第 2∼n+1 行,每行两个整数 ci,di 表示 i 朵云的价钱和价值。

第 n+2∼n+1+m 行,每行两个整数 ui,vi ,表示买 ui 就必须买 vi ,同理,如果买 vi 就必须买 ui

输出格式

一行,表示可以获得的最大价值。

把所有一组物品用并查集合成一块跑背包

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e4+5;
int p[N],v[N],w[N],f[N];//最坏没有超过int范围

int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int n,k,m;//k为搭配,m为有多少钱
	cin>>n>>k>>m;
	for(int i=1;i<=n;i++) p[i]=i;//初始化,忘了这个调了半天
	
	for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
	for(int i=1;i<=k;i++){
		int a,b;//买a必须买b
		cin>>a>>b;
        int pa=find(a),pb=find(b);//预处理好,防止多次查找
		if(pa!=pb){//一定要不同组才能继续合并,不然会重复合并
		//p[find(a)]=find(b);
		v[pb]+=v[pa];
		w[pb]+=w[pa];
		p[pa]=pb;//应当将物品叠加后再并入该组,不然会丢失
		}
	}
	for(int i=1;i<=n;i++)//循环物品
	if(p[i]==i)//当前物品是该物品组根结点
	for(int j=m;j>=v[i];j--)//循环体积
	f[j]=max(f[j],f[j-v[i]]+w[i]);
	
	cout<<f[m];
	return 0;
}
P1955 [NOI2015] 程序自动分析

在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。

考虑一个约束满足问题的简化版本:假设 𝑥1,𝑥2,𝑥3,⋯代表程序中出现的变量,给定 𝑛 个形如 𝑥𝑖=𝑥𝑗 或 𝑥𝑖≠𝑥𝑗 的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:𝑥1=𝑥2,𝑥2=𝑥3,𝑥3=𝑥4,𝑥4≠𝑥1​,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。

现在给出一些约束满足问题,请分别对它们进行判定。

输入格式

输入的第一行包含一个正整数 𝑡,表示需要判定的问题个数。注意这些问题之间是相互独立的。

对于每个问题,包含若干行:

第一行包含一个正整数 𝑛,表示该问题中需要被满足的约束条件个数。接下来 𝑛n 行,每行包括三个整数 𝑖,𝑗,𝑒,描述一个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 𝑒=1,则该约束条件为 𝑥𝑖=𝑥𝑗​。若𝑒=0,则该约束条件为 𝑥𝑖≠𝑥𝑗。

输出格式

输出包括 𝑡 行。

输出文件的第 𝑘 行输出一个字符串 YES 或者 NO(字母全部大写),YES 表示输入中的第 𝑘个问题判定为可以被满足,NO 表示不可被满足。

由于数据过大,先要进行离散化处理,因为数据没有有序的要求,因此可以用哈希表来映射

接着就是并查集来将这一类相等的数进行合并

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <unordered_map>//哈希表
using namespace std;
const int N=2e5+7;
int n,p[N],k;
unordered_map<int,int> mp;
struct query{
	int i,j,e;
}st[N];

int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}

int get(int x){
	if(mp.count(x)==0) mp[x]=++n;
	return mp[x];//返回离散化映射坐标
}

int main(){
	int T;cin>>T;
	while(T--){
		n=0;
		mp.clear();
		cin>>k;
		bool flag=true;//先通过离散化确定n
		for(int i=1;i<=k;i++){
		int x,y,e;
		cin>>x>>y>>e;
		st[i]={get(x),get(y),e};//一定要先离散化来得到个数n
		}
		
		for(int i=1;i<=n;i++) p[i]=i;
		
		for(int m=1;m<=k;m++)
		if(st[m].e==1){
			int pa=find(st[m].i),pb=find(st[m].j);
			p[pa]=pb;
		}
		
		for(int m=1;m<=k;m++)
		if(st[m].e==0){
			int pa=find(st[m].i),pb=find(st[m].j);
			if(pa==pb) flag=false;
		}//一定要先合并完再判断,不然连通性会乱
		
		if(flag) cout<<"YES"<<'\n';
		else cout<<"NO"<<'\n';
	}
	return 0;
}
相关推荐
努力学习编程的伍大侠2 分钟前
基础排序算法
数据结构·c++·算法
XiaoLeisj30 分钟前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
Jasmine_llq1 小时前
《 火星人 》
算法·青少年编程·c#
闻缺陷则喜何志丹1 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
Lenyiin1 小时前
01.02、判定是否互为字符重排
算法·leetcode
鸽鸽程序猿2 小时前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列
Jackey_Song_Odd2 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
Watermelo6172 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
乐之者v2 小时前
leetCode43.字符串相乘
java·数据结构·算法