ABC 175 F - Making Palindrome

n n 个串,每个串 s i s_i 每次的使用费用为 c i c_i ,求最小的形成回文串的费用.
n 50 , s i 20 n\le 50,|s_i|\le 20 .

比赛的时候没做出来,真是降智…

定义 f [ i ] [ j ] [ k ] f[i][j][k] 表示 ( i , j , k ) (i,j,k) 这个状态到回文串还需要多少费用,其中 i i 表示串的种类, j j 表示下标, k k 表示方向(正反).
每次我们都考虑相反方向接什么串,就可以容易得到转移方程.
注意:当自己已经回文了就不用找别的串了.

记忆化搜索实现 D P DP ,总复杂度为 O ( ( s i ) 2 ) O((\sum |s_i|)^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;
}

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/108094924
ABC