现在有一个序列,每个元素是一个集合,集合中可能有p,r,n三种元素。你有5种操作:
1. 删除连续的若干个元素中的p
2. 删除连续的若干个元素中的r
3. 删除连续的若干个元素中的n
4. 直接删除连续的若干个元素
5. 添加一个新元素
其中,前三种操作花费为2,后两种操作花费为1。连续的定义是说,他们在原序列中是挨着的,并且每个元素中都含有对应的元素。
问把原序列变成等长的空集合的序列所需要的最小操作费用。
dp[i][j][k]表示前i个元素变成长度为j的序列,且状态为k的最小操作费用。k代表之前的最后一个操作有没有进行p,r,n,d等操作。d代表删除这个串。
其实不状态压缩也可以..因为要压缩的状态个数是恒定为4个的..跟输入没关系..
但是状态压缩比较好写的样子
#include <cstdio>
#include <cstring>
int dp[510][510][16];
int cal[16][16]={0};
int a[510];
int n;
inline void update(int &a,int b) {
if (a==-1||a>b) a=b;
}
int main() {
int i,j,k;
for (i=0;i<16;i++)
for (j=0;j<16;j++) {
if ((j&1)&&!(i&1)) cal[i][j]+=2;
if ((j&2)&&!(i&2)) cal[i][j]+=2;
if ((j&4)&&!(i&4)) cal[i][j]+=2;
if ((j&8)&&!(i&8)) cal[i][j]++;
}
//calculate cal[][]
while (scanf("%d",&n)!=EOF) {
for (i=1;i<=n;i++) {
char s[30];
scanf("%s",s);
int tmp=0;
for (j=0;s[j]!='\0';j++) {
if (s[j]=='p') tmp|=1;
else if (s[j]=='r') tmp|=2;
else if (s[j]=='n') tmp|=4;
}
a[i]=tmp;
}
int ans=-1;
memset(dp,-1,sizeof(dp));
dp[0][0][0]=0;
for (i=0;i<n;i++) {
int kk=a[i+1];
for (j=0;j<n;j++) {
for (k=0;k<16;k++) {
if (dp[i][j][k]!=-1) {
//printf("%d %d %d %d\n",i,j,k,dp[i][j][k]);
update(dp[i+1][j+1][kk],dp[i][j][k]+cal[k][kk]);
update(dp[i+1][j][k|8],dp[i][j][k]+cal[k][k|8]);
}
}
}
}
for (j=0;j<=n;j++) {
for (k=0;k<16;k++) {
if (dp[n][j][k]!=-1) {
update(ans,dp[n][j][k]+n-j);
}
}
}
if (ans==0) printf("Impossible!\n");
else if (ans>200) printf("pia2de4!\n");
else {
ans--;
while (ans--) printf("re-");
printf("rejudge\n");
}
}
return 0;
}