链接: http://acm.hdu.edu.cn/showproblem.php?pid=6212
题意: 现在有一个01串,保证相同的连续不会超过2,现在可以向1其中的任意一个位置包括两端插入一个0 或者1 ,如果组成3个或以上的连续的相同,则可以消除这个,然后剩下的接起来。直到所有的拿完,问最少插入几个?
思路: 200 区间dp呀,dp[ l ][ r ] 表示从l到r 最少需要插入多少。
第一种,我们可以把l r 分为两个区间 [l k] [k+1 r]
第二种,如果两端的相同把中间的拿掉
if(a[l]==a[r]){
if(cnt[l]+cnt[r]<=2) res=min(res,dfs(l+1,r-1)+1);
else res=min(res,dfs(l+1,r-1));
}
第三种: 如果两端的相同,并且中间有一个与两端的相同,拿掉中间和两端的两个空隙,然后中间的k和两端接起来,并且要求
cnt[ l ]+cnt[r] <3 .
另外这个题好像应该把相同的连续的点缩成一个点、之前没有这样写,好像TLE了。也不确定是不是这里的问题。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =205;
const int inf = 0x3f3f3f3f;
int dp[N][N];
char s[N];
int a[N];
int cnt[N];
int len;
int n;
int dfs(int l,int r)
{
if(l>r) return 0;
if(dp[l][r]!=-1) return dp[l][r];
if(l==r){
if(cnt[l]==1) return dp[l][r]=2;
else if(cnt[l]==2) return dp[l][r]=1;
}
if(r==l+1){
if(cnt[l]==1&&cnt[r]==1) return dp[l][r]=4;
else if(cnt[l]==1&&cnt[r]==2) return dp[l][r]=3;
else if(cnt[l]==2&&cnt[r]==1) return dp[l][r]=3;
else if(cnt[l]==2&&cnt[r]==2) return dp[l][r]=2;
}
int res=inf;
for(int k=l;k<r;k++){
res=min(res,dfs(l,k)+dfs(k+1,r));
}
if(a[l]==a[r]){
if(cnt[l]+cnt[r]<=2) res=min(res,dfs(l+1,r-1)+1);
else res=min(res,dfs(l+1,r-1));
for(int k=l+1;k<r;k++){
if(a[k]==a[l]&&cnt[k]==1){
if(cnt[l]+cnt[r]<4) res=min(res,dfs(l+1,k-1)+dfs(k+1,r-1));
}
}
}
return dp[l][r]=res;
}
void init()
{
for(int i=0;i<=len+2;i++){
cnt[i]=a[i]=0;
}
}
int main()
{
int T;
scanf("%d",&T);
int kk=0;
while(T--)
{
scanf("%s",s+1);
len=strlen(s+1);
init();
n=0;
cnt[++n]=1;
a[n]=s[1]-'0';
for(int i=2;i<=len;i++){
if(s[i]==s[i-1]) cnt[n]++;
else{
cnt[++n]=1;
a[n]=s[i]-'0';
}
}
for(int i=0;i<=n+2;i++){
for(int j=0;j<=n+2;j++) dp[i][j]=-1;
}
int ans=dfs(1,n);
printf("Case #%d: %d\n",++kk,ans);
}
return 0;
}