SCOI2007 压缩题解
Preface
给出一种耗时更大的"新颖“做法。
Description
给你一个字符串,可以用题目中给的方式进行合并,问如何合并使得合并后的字符串长度最短。
Method
我们看到了 ,又注意到题目是对区间进行操作,我们想到了什么?区间DP。
DP sequence
如果学过区间DP,应该知道是小区间合并成大区间。
DP state transition equation
Explanation
---表示在这一段区间内一个M也没有所能达到的最小字符串长度。
---表示在这一段区间内只有最左边有M所能达到的最小字符串长度。
---表示在这一段区间内除了最左边还有其他地方有M所能达到的最小字符串长度。
---一个M也没有只能从其他地方一个M也没有转移过来。
---最左边有M必须是左边的区间最左边有M,且右边的区间一个M也没有。
---除了最左边的其他地方有M,只要强制右区间有M即可达到。
---这就是题目的条件,也是本题的最难点,我们可以发现,每多一个R,序列长度就会翻倍,那么我们枚举他翻几倍,就是多了几个R,然后在分类讨论,如果前面的区间一个M都没有,那么就需要加一个M,如果前面有M,那么就不需要加M。
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
/* f[lft][rit][p] [lft,rit] 区间
p=0 表示整个区间没有M
p=1 表示只有最左边有M
p=2 表示在最左边的其他地方还有M
*/
string s;
int ans3,ans4,ans1,ans2,x,y,digit[1000],n,i,j,k,lft,rit,len,f[110][110][3],pw[200],a[1000];
bool check(int l,int r,int len)
{
int x,i;
for (i=l;i<=r;i++)
{
x=(i-l) % len+l;
if (a[i]!=a[x]) return false;
}
return true;
}
int main()
{
cin>>s;
pw[0]=1;
for (i=1;i<=20;i++) pw[i]=pw[i-1]*2;
n=s.size();
for (i=1;i<=100;i++)
{
for (j=i;j;j/=10) digit[i]++;
}
for (i=1;i<=n;i++) a[i]=s[i-1]-'a'+1;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
f[i][j][0]=100000000,f[i][j][2]=100000000,f[i][j][1]=100000000;
for (i=1;i<=n;i++)
if (i==1)
{
f[i][i][0]=1;f[i][i][1]=1;
}
else f[i][i][0]=1;
for (k=2;k<=n;k++)
{
for (lft=1;lft<=n-k+1;lft++)
{
rit=lft+k-1;
f[lft][rit][0]=rit-lft+1;
for (i=lft;i<rit;i++)
{
x=i-lft+1;y=rit-i;
ans1=f[lft][i][1];
ans2=f[lft][i][0];ans3=f[i+1][rit][1];ans4=f[i+1][rit][0];
/*for (j=1;j<=20;j++)
if (x % pw[j]==0)
if (check(lft,i,x/pw[j]))
{
len=x/pw[j];
if (lft==1)
{
ans1=min(ans1,f[lft][lft+len-1][0]+j);
}
else
{
ans1=min(ans1,f[lft][lft+len-1][0]+j+1);
ans1=min(ans1,f[lft][lft+len-1][1]+j);
}
}
for (j=1;j<=20;j++)
if (y % pw[j]==0)
if (check(i+1,rit,y/pw[j]))
{
len=y/pw[j];
ans3=min(ans1,f[i+1][i+1+len-1][0]+j+1);
ans3=min(ans1,f[i+1][i+1+len-1][1]+j);
}*/
f[lft][rit][0]=min(f[lft][rit][0],ans2+ans4);
f[lft][rit][1]=min(f[lft][rit][1],ans1+ans4);
f[lft][rit][2]=min(f[lft][rit][2],min(min(ans1,ans2),f[lft][i][2])+min(ans3,f[i+1][rit][2]));
//if ((lft==1)&(rit==5)) cout<<f[lft][rit][1]<<" "<<f[lft][i][1]<<" "<<ans1<<" "<<ans4<<" "<<i<<endl;
//f[lft][rit]=min(f[lft][rit],f[lft][i]+f[i+1][rit]);
}
len=rit-lft+1;
//cout<<pw[1]<<endl;
for (j=1;j<=20;j++)
if (len % pw[j]==0)
if (check(lft,rit,len/pw[j]))
{
f[lft][rit][1]=min(f[lft][rit][1],f[lft][lft+(len/pw[j])-1][1]+j);
f[lft][rit][1]=min(f[lft][rit][1],f[lft][lft+(len/pw[j])-1][0]+j+1);
}
}
}
cout<<min(min(f[1][n][1],f[1][n][2]),f[1][n][0])<<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下