$P2453 [SDOI2006] $最短距离

#$Description$ [题面](https://www.luogu.org/problem/P2453) 题面比较复杂,大概就是有几个操作函数要求将起始字符串变换为目标字符串,每个操作有代价,求代价最小值 #$Solution$ 其实很多状态压缩类动态规划可以转换成最短路问题,只需要将状态压缩为一个点就行,一般处理变换操作可以使用这种方法。

实际上最短路问题就是在图上跑(DP),但由于(DP)转移受顺序限制需要在拓扑序上跑,而(dijkstra)只需要指定从起点到终点就行。

在这类题中,我们建出来的图就是一个状态转移图,而一般的状压(DP)就是在图上跑(DP),和(dijkstra)是等效的。

对于这个题,关键点在于如何设计状态和连边建图

(Solution)

这道题有一个比较显然的性质:就对于最优变换方案,每个时刻目标串一定是最终串的前缀,源串一定是起始串的后缀。

证明:假设当前目标串不是最终串的前缀,那么有两种可能:
(1.)长度超过最终串:那就必须删除,这和在插入前删除是等效的
(2.)目标串有字符不匹配最终串,那么之后还需要(delete)或者按之前的路修改回去,不如提前就修改或者直接(delete)

有了这个性质,状态就比较好表示了,只有((lena+1) imes(lenb+1))种状态

Me0ip4.png

状态计算函数就是这个

int id(int x,int y)//x是目标串长度,y是源串长度 
{
	return x*(lena+1)+y+1;
}

考虑如何连边

操作一、二、四是比较显然的,操作三要注意判断一下,相同才能连边,操作五同理,对于操作清空要特别注意,如果你直接(id(i,j)->id(i,0))是错误的,假设(j)(0),那么根据题面就可以不断删除,而代价是(-1),所以要特别判断这个情况

void link()
{
	for(re int i=0;i<=lenb;++i)
	 for(re int j=0;j<=lena;++j)
	 {
	 	if(j>=1) add(id(i,j),id(i,j-1),w[1]);//del
	 	if(j>=1&&i<lenb)
	 	{
	 		add(id(i,j),id(i+1,j-1),w[2]);//replace任何情况都行 
	 		if(B[i+1]==A[lena-j+1]) add(id(i,j),id(i+1,j-1),w[3]);//相等则可以直接copy 
		}
		if(i<lenb)
		{
			add(id(i,j),id(i+1,j),w[4]);
		}
		if(j>=2&&i<=lenb-2)
		{
			if(B[i+1]==A[lena-j+2]&&B[i+2]==A[lena-j+1])
			add(id(i,j),id(i+2,j-2),w[5]);//判断 
		}
		if(i==lenb&&j!=0)//这里一定要注意 
		{
			add(id(i,j),id(i,0),j*w[1]-1);
		}
	 }
}

然后加个(dijkstra)(id(0,lena))跑到(id(lenb,0))即可

完整代码

(Code)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#define re register
#define maxn 200010
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct Edge{
	int v,w,nxt;
}e[maxn<<2];
int w[101],cnt,head[maxn],vis[maxn];
char A[220],B[220];
int lena,lenb,T,S,dis[maxn];
int id(int x,int y)//x是目标串长度,y是源串长度 
{
	return x*(lena+1)+y+1;
}
inline void add(int u,int v,int w)
{
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
struct node{
	int u,d;
	bool operator <(const node&rhs) const{
		return rhs.d<d;
	}
};
priority_queue<node> q;
void dijkstra(int s)
{
	while(!q.empty()) q.pop();
	q.push((node){s,0});
	memset(dis,INF,sizeof(dis));
	dis[s]=0;
	while(!q.empty())
	{
		node now=q.top();
		q.pop();
		if(vis[now.u]) continue;
		vis[now.u]=1;
		for(int i=head[now.u];i;i=e[i].nxt)
		{
			int ev=e[i].v;
			if(dis[ev]>dis[now.u]+e[i].w)
			{
				dis[ev]=dis[now.u]+e[i].w;
				q.push((node){ev,dis[ev]});
			}
		}
	}
}
void link()
{
	for(re int i=0;i<=lenb;++i)
	 for(re int j=0;j<=lena;++j)
	 {
	 	if(j>=1) add(id(i,j),id(i,j-1),w[1]);//del
	 	if(j>=1&&i<lenb)
	 	{
	 		add(id(i,j),id(i+1,j-1),w[2]);//replace任何情况都行 
	 		if(B[i+1]==A[lena-j+1]) add(id(i,j),id(i+1,j-1),w[3]);//相等则可以直接copy 
		}
		if(i<lenb)
		{
			add(id(i,j),id(i+1,j),w[4]);
		}
		if(j>=2&&i<=lenb-2)
		{
			if(B[i+1]==A[lena-j+2]&&B[i+2]==A[lena-j+1])
			add(id(i,j),id(i+2,j-2),w[5]);//判断 
		}
		if(i==lenb&&j!=0)//这里一定要注意 
		{
			add(id(i,j),id(i,0),j*w[1]-1);
		}
	 }
}

int main()
{
    cin>>A+1,lena=strlen(A+1);
    cin>>B+1,lenb=strlen(B+1);
    for(re int i=1;i<=5;++i) w[i]=read();
    S=id(0,lena);
    T=id(lenb,0);
    link();
    dijkstra(S);
    printf("%d
",dis[T]);
	return 0;
}

原文地址:https://www.cnblogs.com/Liuz8848/p/11824466.html