Cry_For_theMoon  

传送门

样例弱得一批。模拟赛T2,上演了大型fst现场()。

题意:

给定两个长度不超过 \(4000\) 的字符串 \(s,t\),可以进行若干次下面的操作:

  • 花费 \(a\) 的代价,在 \(s\) 的一个位置添加一个任意字符。
  • 花费 \(b\) 的代价,删除 \(s\) 的一个字符。
  • 花费 \(c\) 的代价,修改 \(s\) 的一个字符。
  • 花费 \(d\) 的代价,交换 \(s\) 的两个相邻的字符。

其中保证 \(a+b \le 2d\)

问最少的代价,把 \(s\) 变成 \(t\)

\(1 \le a,b,c,d \le 100\)​。

分析:

如果只有前三种操作那是一个 trival 的题。

交换操作是本题最大的毒瘤。

结论:

\(1.\)​​​ 一个字符,如果它是被创造的,那么不会再被操作(交换也属于操作)。一个字符如果被替换过一次,那么一定不会再对它操作。

证明:如果创造过后被删除,没有任何意义。如果创造过后被修改,为什么不创造的时候就修改。如果创造过后被交换,比如说本来有一个字符 \(b\),然后前面创造一个字符 \(a\),得到 \(ab\),交换得到 \(ba\)。那为什么不直接一开始在 \(b\) 的后面创造字符。

如果一个字符原来存在,然后被修改了,显然删除它和再次修改它没有任何意义。考虑被修改后交换,则代价为 \(c+d\)​,比如说原来为 \(bc\)​,我们修改成 \(ac\)​,交换得到 \(ca\)​,首先发现 \(a+b \le 2d\)​,如果 \(c \ge d\)​,那么我们直接删除 \(b\)​,在 \(c\)​ 后面加入一个 \(a\)​,代价为 \(a+b \le 2d \le c+d\)​。如果 \(c \le d\)​,我们直接把 \(bc\)​ 做两次替换得到 \(ca\)​,代价为 \(2c \le 2d\)​。

这个结论告诉我们什么?告诉我们,我们只会交换 \(s\) 中本来存在的字符。

\(2.\) 如果 \(s_1\)\(s_2\) 交换了,则这两个字符不会再被交换。也不会再被修改

证明:先说明不会被交换,不妨采用反证。不失一般性,假设交换后,\(s_2\)​ 位置(字符是原来的 \(s_1\))又和 \(s_3\) 交换了。即 \(abc\),先变成 \(bac\),然后变成 \(bca\)

然后你发现其实可以通过删除最前面的 \(a\),加入最后面的 \(c\) 来实现,代价是 \(a+b \le 2d\)​。

然后再说明不会被修改。如果我们交换 \(ab\) 变成 \(ba\)。首先考虑只修改一个字符的情况,容易发现这是无意义的,一次删除+一次添加(创造)就能实现同样效果且花费更小。再来考虑两个字符都被修改的情况,代价是 \(d+2c\)。那为什么不交换前就修改好,花费 \(2c\) 代价呢。

做法:

模拟赛时,我 yy 出了这两个结论。正常人到这里,都能 AC 了,但是我还是 fst 了 /kk。

还是设 \(f(i,j)\)​ 为 \(s\) 的前 \(i\) 个字符和 \(t\) 的前 \(j\)​ 个字符相等的最少代价。前三种转移非常简单:

  • 添加:\(f(i,j)=f(i,j-1)+a\)
  • 删除:\(f(i,j)=f(i-1,j)+ b\)
  • 修改:\(f(i,j)=f(i-1,j-1)+c\)
  • 特殊地,\(s_i=t_j\) 的时候,额外考虑 \(f(i,j)=f(i-1,j-1)\)

然后考虑加入第四种操作。根据结论 \(1\) 和结论 \(2\)。我们会选择 \(s_i\) 之前的一个字符 \(s_k\)\(s_{k+1}\sim s_{i-1}\) 的字符删掉(一位神仙的 fst 原因), 交换 \(s_k\)\(s_i\)。然后再在它们之间添加若干个字符(我的fst原因)。根据结论 \(2\),这两个字符不能被修改。那么 \(s_k\) 必须等于 \(t_j\)。设 \(s_i\)\(t_y\) 匹配,那么 \(s_i\) 必须等于 \(t_y\)

考虑此时的代价是:\(f(k-1,y-1)+(i-k-1)\,\times b+(j-y-1)\times\,a\)

我们发现,对于一个固定的 \(i,j\) 来说,会尽可能让 \(k\)\(y\) 尽可能的大。所以我们预处理一个 \(g(i,j)\),维护一个 pair,储存对应的 \(k,y\)。计算 \(f(i,j)\) 的时候直接从 \(g(i,j)\) 的信息里转移过来即可。至于 \(g(i,j)\) 的计算,可以通过维护两个桶来实现。

复杂度:\(O(n^2)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define per(i,a,b) for(ll i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
using namespace std;
const int MAXN=4010,INF=1e9;
int a,b,c,d,n,m,f[MAXN][MAXN];
pi g[MAXN][MAXN];
int bucket1[26],bucket2[26];
char s[MAXN],t[MAXN];
void calc(int i,int j){
	if(s[i]==t[j])f[i][j]=Min(f[i][j],f[i-1][j-1]);
	//1.插入
	f[i][j]=Min(f[i][j],a+f[i][j-1]); 
	//2.删除
	f[i][j]=Min(f[i][j],b+f[i-1][j]);
	//3.替换
	f[i][j]=Min(f[i][j],c+f[i-1][j-1]);
	//4.交换
	pi zero=mp(0,0);
	if(g[i][j]!=zero){
		int x=g[i][j].fr,y=g[i][j].se;
		//sx和si交换
		//sx对应tj,si对应ty
		int cost=d+(i-x-1)*b+(j-y-1)*a; 
		f[i][j]=Min(f[i][j],f[x-1][y-1]+cost); 
	} 
}
int main(){
	scanf("%d%d%d%d",&a,&b,&c,&d);
	scanf("%s%s",s+1,t+1);
	n=strlen(s+1);
	m=strlen(t+1);
	rep(i,0,n)rep(j,0,m)f[i][j]=INF,g[i][j]=mp(0,0);
	f[0][0]=0;
	rep(i,1,n)f[i][0]=i*b;
	rep(i,1,m)f[0][i]=i*a;
	rep(i,1,n){
		memset(bucket2,0,sizeof bucket2);
		rep(j,1,m){
			if(bucket1[t[j]-'a'] && bucket2[s[i]-'a']){
				g[i][j]=mp(bucket1[t[j]-'a'],bucket2[s[i]-'a']);
			}
			bucket2[t[j]-'a']=j;
		}
		bucket1[s[i]-'a']=i;
	}
	rep(i,1,n){
		rep(j,1,m){
			calc(i,j);
		}
	}
	printf("%d",f[n][m]);
	return 0;
}

最后模拟赛还是 T1 + T3 200 pts 滚出了,555

posted on 2021-08-12 13:09  Cry_For_theMoon  阅读(68)  评论(0编辑  收藏  举报