LC.CF GYM100739J.Longest cheap palindrome
我们设\(f[i,j,k,l,r]\)表示:
当前左端取到了位置\(i\),右端取到了位置\(j\);
当前选择的子序列长度为\(k\);
区间\([i,l],[r,j]\)中所有字符都被选择时,最小要付出的代价。
转移很简单,枚举左右两边下一个字符选到哪里即可。
这里有一份\(O(n^8)\)的代码,按理说是过不去的,但是因为每一重循环内部都剪掉了很多枝,所以最终的结果是过掉了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,lim,res;
ll cost[34][34],f[2][34][34][34][34];//f[k,i,j,l,r]:leftmost at i, rightmost at j, length of 2k, [i,l] and [r,j] have been chosen
char s[50];
int main(){
scanf("%d%d%d",&n,&m,&lim),memset(f,0x3f,sizeof(f));
scanf("%s",s+1);
for(int i=1,a,b,c;i<=m;i++)scanf("%d%d%d",&a,&b,&c),cost[a][b]+=c;
for(int i=1;i<=n;i++)for(int j=i+2;j<=n;j++)if(s[i]==s[j])f[1][i][j][i][j]=cost[i][i]+cost[j][j];
for(int k=1;(k<<1)<=n;k++){
for(int i=1,j=i+(k<<1)-1;j<=n;i++,j++){
bool ok=true;
for(int l=0;l<k;l++)ok&=(s[i+l]==s[j-l]);
if(!ok)continue;
f[k&1][i][j][i+k-1][i+k]=0;
for(int u=i;u<=j;u++)for(int v=u;v<=j;v++)f[k&1][i][j][i+k-1][i+k]+=cost[u][v];
}
memset(f[!(k&1)],0x3f,sizeof(f[!(k&1)]));
for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)for(int l=i;l<=j;l++)for(int r=j;r>l;r--){
if(f[k&1][i][j][l][r]>lim)continue;
res=max(res,k<<1);
for(int u=i-1;u;u--)for(int v=j+1;v<=n;v++){
if(s[u]!=s[v])continue;
if(l+1==r&&u==i-1&&v==j+1)continue;
ll now=f[k&1][i][j][l][r];
if(u==i-1)for(int w=u;w<=(l+1==r?j:l);w++)now+=cost[u][w];
else now+=cost[u][u];
if(v==j+1)for(int w=v;w>=(l+1==r?i:r);w--)now+=cost[w][v];
else now+=cost[v][v];
f[!(k&1)][u][v][u==i-1?l:u][v==j+1?r:v]=min(f[!(k&1)][u][v][u==i-1?l:u][v==j+1?r:v],now);
}
}
}
printf("%d\n",res);
return 0;
}