书接上一回,继续做题:
字典序最小问题:Best Cow Line ( POJ 3617 )
最开始的想法是:对比首尾的字符大小,选小的放入T中
而当首尾字符相同的时候,对比第二个和倒数第二个的字符,选小的放入T中,如果还相同,则对比第三个和倒数第三个字符,一次类推。如果都相等的话,则是回文串,任意选两边都可以。
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
int n,i,a,b;
char s[2010];
while(~scanf("%d",&n))
{
scanf("%s",s);
a=0,b=n-1;
while(a<=b) //包含a==b是为了输出最后一个
{
bool left=false;
for(i=0;a+i<=b;i++)
{
if(s[a+i]<s[b-i])
{
left=true;
break;
}
else
{
if(s[a+i]>s[b-i])
{
left=false;
break;
}
}
}
if(left) printf("%c",s[a++]);
else printf("%c",s[b--]);
}
printf("\n");
}
}
对于奇数,如:ABCBA
最后比较的是C和C,相等,放入默认的一边
对于偶数,如:ABCCBA
最后比较的还是C和C,也是相等的,放入默认的一边
Saruman 's Army ( POJ 30669 )
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,r,x[1005];
int main()
{
scanf("%d%d",&n,&r);
int i,ans=0;
for(i=0;i<n;i++) scanf("%d",&x[i]);
sort(x,x+n);
i=0;
while(i<n)
{
int s=x[i++]; //s为没有被覆盖的最左的点的位置
while(i<n&&x[i]<=s+r) i++;
int p=x[i-1]; //p是新加上标记的点的位置
while(i<n&&x[i]<=p+r) i++;
ans++;
}
printf("%d\n",ans);
}
书上给出的代码是要记录两个点,一个是开始的点,s,另一个是标记的点,p
考虑到两个while写的内容一样,能否只用一个标记点呢?毕竟只是找每个点的下一个合适的点,把新点的数据覆盖旧点就可以了,这样一个s就能重复用。
我尝试只用一个点,从12行开始改写:
i=0;
s=x[i++];
while(i<n)
{
while(i<n&&x[i]<=s+r) i++;
//判断退出条件 两个判断条件不能同时等价
if(i==n) break;//说明x[i]走到尽头,一直都<=s+r
else
{ //x[i]>s+r
s=x[i++]; //新的起点
ans++;
}
}
但输入
R=0 N=3 x={ 10 , 20 , 20 } 后
我的代码输出的是1,而正确答案是2
原因是每找到一个新的起点,ans才增一。而10 20 20 这一组,新的起点只有第一个20,想继续找下一个新起点时,i已经等于n,找不到第二个新起点。所以ans只等于1。
回看书上的代码,找到p=10为标记点后ans加一,又找到20为标记点,ans又加一,所以ans=2。另外,p=20之后,用while一直寻找距离20超过0的点,直到走到尽头也没有找到,程序结束。
这题涉及一点二叉树的知识:
Fence Repair ( POJ 3253 )
思路是:
如果两块板是最短和次短的,那么他们的消耗是最少的,记录下这两块板的总长,把这两块板拼合当做一块板后,再在剩下的n-1块板中找到最短和次短的,记录下这两块板的总长,把最短和次短拼合成另一块板,再在剩下n-2块板中寻找。直到最后只剩一块板。
可以尝试别的切割方法来感受一下。
代码如下:
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
int i,n,ans=0,t,L[50005],min1,min2;
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%d",&L[i]);
while(n>1)
{ //min1为最小值 min2为次小值
min1=0;min2=1;
if(L[min1]>L[min2]) swap(min1,min2);
//找出最小值和次小值
for(i=2;i<n;i++)
{
if(L[i]<L[min1]) //若当前值比已知最小值还要小
{
min2=min1;//现在的次小值的下标就是旧的最小值的下标
min1=i;//更新最小值的小标
}
else if(L[i]<L[min2]) //L[i]>=L[min2]&&L[i]<L[min2]
min2=i; //更新次小值的下标
}
//现在知道最小值和次小值的下标
t=L[min1]+L[min2]; //t为两块最小和次小木板的长度和
ans+=t; //统计答案
//更新L数组
if(min1==n-1) swap(min1,min2);
//因为L[min1]和L[min2]这两块木板已经合并,所以这两个数组要更新
L[min1]=t; //L[min1]数组用来记录合成的木板长度,形成新的木板
L[min2]=L[n-1]; //L[min2]更改成L[n-1]
n--; //总木板数量减少
}
}
对于代码:if(min1n-1) swap(min1,min2);
是防止min1等于n-1时,在n–后,L[n-1]的值没有保存下来
例如:L={3,4,5,2,1} 时
在第一轮:n=5,min1=4,min2=3,没有这一句的话,接下来:
L[4]=t;
L[3]=L[5-1]; //即L[3]=L[4]=t;
然后n–; // n=4;
L[4] 在接下来就不会搜到,且L[n-1]的值没有保存下来,从而引发错误
加了句之后,如果min2n-1,接下来L[min2]=L[n-1],即自己等于自己,本来下一轮也不会被搜索到,仍不受影响
综上:
被合并的两块木板的空间,一块存放新合并木板的值,另一块存放下一轮即将搜索不到的值