今日学习了最短编辑距离和并查集相关应用
P2758 编辑距离
设 𝐴和 𝐵是两个字符串。我们要用最少的字符操作次数,将字符串 𝐴转换为字符串 𝐵。这里所说的字符操作共有三种:
- 删除一个字符;
- 插入一个字符;
- 将一个字符改为另一个字符。
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;
}