有 个串,每个串 每次的使用费用为 ,求最小的形成回文串的费用.
.
比赛的时候没做出来,真是降智…
定义
表示
这个状态到回文串还需要多少费用,其中
表示串的种类,
表示下标,
表示方向(正反).
每次我们都考虑相反方向接什么串,就可以容易得到转移方程.
注意:当自己已经回文了就不用找别的串了.
记忆化搜索实现 ,总复杂度为 .
#include<bits/stdc++.h>
#define SZ(a) ((int) a.size())
using namespace std;
typedef long long ll;
const int N=55,M=22;
const ll inf=1e18;
int n,v[N][M][2],c[N];
ll f[N][M][2],ans=inf;
string s[N];
ll dp(int x,int y,int d) {//x为串编号,y为位置,d为方向(正反)
if(y==SZ(s[x])) return 0;
int &u=v[x][y][d];
ll &v=f[x][y][d];
if(u) return v;
u=1; v=inf;
bool flag=1;
if(!d) {for(int j=y,k=SZ(s[x])-1;j<k;j++,k--) if(s[x][j]!=s[x][k]) {flag=0; break;}}
else {for(int j=y,k=0;j>k;j--,k++) if(s[x][j]!=s[x][k]) {flag=0; break;}}
if(flag) return v=0;
for(int i=1,j,k;i<=n;i++) {//枚举另一个方向接哪个串
flag=1;
if(!d) {
for(j=y,k=SZ(s[i])-1;j<SZ(s[x])&&k>=0;j++,k--)
if(s[x][j]!=s[i][k]) {flag=0; break;}
if(flag) {
if(k<0) v=min(v,c[i]+dp(x,j,0));
else v=min(v,c[i]+dp(i,k,1));
}
}
else {
for(j=y,k=0;j>=0&&k<SZ(s[i]);j--,k++)
if(s[x][j]!=s[i][k]) {flag=0; break;}
if(flag) {
if(j<0) v=min(v,c[i]+dp(i,k,0));
else v=min(v,c[i]+dp(x,j,1));
}
}
}
return v;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) cin>>s[i]>>c[i];
for(int i=1;i<=n;i++)
ans=min(ans,c[i]+dp(i,0,0));
printf("%lld\n",ans==inf?-1:ans);return 0;
}