$P2453\ [SDOI2006]\ $最短距离

\(Description\)

题面
题面比较复杂,大概就是有几个操作函数要求将起始字符串变换为目标字符串,每个操作有代价,求代价最小值

\(Solution\)

其实很多状态压缩类动态规划可以转换成最短路问题,只需要将状态压缩为一个点就行,一般处理变换操作可以使用这种方法。

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

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

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

\(Solution\)

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

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

有了这个性质,状态就比较好表示了,只有\((lena+1)\times(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\n",dis[T]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Liuz8848/p/11824466.html
今日推荐