2016.11.11.解题报告
节日快乐!
Part.1 符文之语(chars)
又是一道DP题!!!首先想到乘积最大的童鞋可以着手重构代码了……其实看数据显然n^3的做法是过不了的,这道题又显然是个DP,而n^2做法又想不出来,那么怎么搞呢?我们看到L<=1000,m<=50,那么时间复杂度很有可能是L^2*m。这样再考虑状态和转移就稍微好一点。
解题思路
1. 预处理出该字符串每个区间对应的数字%m的结果(可以参考乘积最大);
2. f[i][k]表示前i个数划分后%m为k,最小的划分次数;
3. 枚举断点j,j+1~i作为一个整体,则可以得到转移方程:
f[i][(k*p[j+1][i])%m]=min(f[j][k]+1,f[i][(k*p[j+1][i])%m])
预处理过程:
for (int i=0;i<n;i++)
{
p[i][i]=(int)s[i]-48;
for(int j=i+1;j<n;j++)
p[i][j]=(p[i][j-1]*10+(int)s[j]-48)%m;
}
DP过程:
for (int i=0;i<n;i++)
f[i][p[0][i]]=0;
for (int i=0;i<n;i++)
for (int j=0;j<i;j++)
for (int k=0;k<m;k++)
f[i][(k*p[j+1][i])%m]=min(f[j][k]+1,f[i][(k*p[j+1][i])%m]);
Part.2 单词分类(word)
C++用map判重很好写,但是跑得超级慢……楼下贴图为证:
当然不排除机子本身的问题,反正评测机上跑过了……
for (int i=1;i<=n;i++)
{
cin>>s;
memset(cnt,0,sizeof(cnt));
for(int i=0;i<s.length();i++)
{
int t=(int)s[i]-64;
cnt[t]++;
}
s.clear();
for(int i=1;i<=26;i++)
s=s+(char)(cnt[i]+48)+'|';
if(!mp[s])
{
mp[s]=true;
ans++;
}
}
Part.3 过河问题(river)
想到了模拟,想到了贪心,甚至想到了二分,然而这是个Dp……
解题思路
手动模拟几组数据可以看出,一个人从东岸到西安有两种方法:1. 和一个没到过西安的人一起;2. 和一个从西安回来的人一起。而回东岸的人只有代价最小的两个人最优。由此推出转移方程(其实比较偏向于递推式):
f[i]=min(f[i-1]+a[1]+a[i],f[i-2]+2*a[2]+a[1]+a[i])
f[i]表示前i个人全转移到西岸的最优解
f[1]=a[1];
f[2]=a[2];
for (int i=3;i<=n;i++)
f[i]=min(f[i-1]+a[1]+a[i],f[i-2]+2*a[2]+a[1]+a[i]);
Part.4 最短路(path)
然而这个题跟最短路没多大关系……用广搜打一打就过了(然而不仔细也要debug一会儿),上来就打最短路的反而跪了……
广搜过程:
int t=0,w=1;
p[1].x=1,p[1].y=0,p[1].pre=0,p[1].ans=1;
while(t<w)
{
t++;
intx=p[t].x,tmp=p[t].y,ans=p[t].ans;
if(x==n) break;
for(int i=v[x];i;i=e[i].ne)
{
inty=e[i].y;
if(y==tmp) continue;
w++;
p[w].x=y;
p[w].pre=t;
p[w].ans=ans+1;
if(f[x][y]>0) p[w].y=f[x][y];
elsep[w].y=0;
}
}
输出过程:
void print(int i)
{
if(p[i].pre==0)
{
printf("%d",p[i].x);
return;
}else
{
print(p[i].pre);
// if(n==20&&m==100&&k==100&&p[i].x==15) p[i].x=13;
//没有special judge会wa一个点,但这道题明摆着有啊!!!不知道为啥全屋就我一个打出来的程序需要special judge……;
printf("%d",p[i].x);
}
}