ABC 175 F - Making Palindrome

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 n50,si20.

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

定义 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
#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;
}

你可能感兴趣的:(搜索)