有 n n n个串,每个串 s i s_i si每次的使用费用为 c i c_i ci ,求最小的形成回文串的费用.
n ≤ 50 , ∣ s i ∣ ≤ 20 n\le 50,|s_i|\le 20 n≤50,∣si∣≤20.
比赛的时候没做出来,真是降智…
定义
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示
(
i
,
j
,
k
)
(i,j,k)
(i,j,k)这个状态到回文串还需要多少费用,其中
i
i
i表示串的种类,
j
j
j表示下标,
k
k
k表示方向(正反).
每次我们都考虑相反方向接什么串,就可以容易得到转移方程.
注意:当自己已经回文了就不用找别的串了.
记忆化搜索实现 D P DP DP,总复杂度为 O ( ( ∑ ∣ s i ∣ ) 2 ) O((\sum |s_i|)^2) O((∑∣si∣)2).
#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;
}