2022-01-10 每日打卡:难题精刷
写在前面
“这些事儿在熟练之后,也许就像喝口水一样平淡,但却能给初学者带来巨大的快乐,我一直觉得,能否始终保持如初学者般的热情、专注,决定了在做某件事时能走多远,能做多好。” 该系列文章由python编写,所刷题目共三个来源:之前没做出来的 ;Leetcode中等,困难难度题目; 周赛题目;某个专题的经典题目,所有代码已AC。每日1-3道,随缘剖析,希望风雨无阻,作为勉励自己坚持刷题的记录。
POJ 2456 Aggressive cows
并没有好的算法来排布,最开始想的是递归二分,每次将一只牛放在区间最接近中位数的位置,也许思路是对的,但超时了。看不见测试用例,这里记录一下双指针(尺取)法:
需要枚举的题几乎都可以使用二分法,即不断逼近以确定答案,只需要将第一次的取值确定好即可。
譬如这个题可以发现距离的上界是a[n-1]- a[0] /(牛的个数-1)
,再超过这个距离一定放不下。
那么就进行不断二分,设下界为1,上界为这个距离的两倍,得出的一个距离就是该距离。然后二分。
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
int a[100005];
int n,m,i,j,f,l,mid;
bool check(int mm)
{
int i,s=1,k=0;
for(i=1;i<n;i++)
{
if(mm+a[k]<=a[i])
{
k=i;
s++;
}
if(s==m)
break;
}
if(s<m)return false;
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
int total = ceil(((a[n - 1] - a[0]) / (double)(m-1)));
f=1,l=total*2-1;
while(f<=l)//二分
{
mid=(f+l)/2;
// cout<<f<<l<<mid;
if(check(mid))
f=mid+1;
else
l=mid-1;
}
printf("%d\n",l);
}
POJ 2406 Power Strings
KMP求最小循环节,首先要理解next数组的含义:next[i]表示前面长度为i的子串中,前缀和后缀相等的最大长度。
可以发现:
- 不相同,
模式串后移,相当于模式串的指针前移
,在这个题中最开始的一个串的表现就是前指针,也就是从开头就匹配的模式串不动(移回来了),后指针++ - 相同,前后指针一起移动
前指针移动到什么位置呢?在KMP算法中,移动到 部分对应匹配值
的下一位就可以,也就是我们说的next数组。所以主要是需要求出next数组:
求法就是dp,共有以下转移条件:
- 开始or不相等:移动指针到匹配个数,赋值next
- 相等:移动指针,赋值next
#include<cmath>
#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
typedef long long LL;
char s[1000000+10];
int nexts[1000000+10];
int len;
void get_next()
{
int i,j;
j=nexts[0]=-1;
i=0;
while(i<len)
{
while(-1!=j&&s[i]!=s[j]) j=nexts[j];
nexts[++i]=++j;
}
}
int main()
{
while(~scanf("%s",s)&&s[0]!='.')
{
len=strlen(s);
get_next();
int ans=1;
if(len%(len-nexts[len])==0)
ans=len/(len-nexts[len]);
printf("%d\n",ans);
}
}