Powered by:AB_IN 局外人
P1147 连续自然数和
旧题新做,老师说用等差数列做,菜鸡正在研究。。
刚学了尺取法,即双指针。
学姐说双指针面试很注重。。
看来得好好学。
先拿水题练练手。
#include <bits/stdc++.h>
using namespace std;
int a[2000010],n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
a[i]=i;
int i=1,j=1;
int sum=a[1];
while(i<=j&&j<n){
if(sum>=n){
if(sum==n) printf("%d %d\n",i,j);
sum-=a[i];//左指针先减数
i++;
}
else{
j++;//右指针先自增
sum+=a[j];
}
}
return 0;
}
奥林oj跑18ms,作弊 +改输入输出跑7ms。O(n)算法果然名不虚传。。
前面有这个题前缀和+二分的做法。(跑的比较慢)
poj3061 Subsequence
题意:给定一个序列,使得其和大于或等于s,求最短的子序列长度。
#include <bits/stdc++.h>
using namespace std;
int a[2000010],n,t,s;
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&s);
int ans=0x3f3f3f3f;
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int i=1,j=1;
int sum=a[1];
while(i<=j&&j<=n){
if(sum>=s){
ans=min(ans,j-i+1);
sum-=a[i];
i++;
}
else{
j++;
sum+=a[j];
}
}
if(ans==0x3f3f3f3f) ans=0;
printf("%d\n",ans);
}
return 0;
}
poj2739 Sum of Consecutive Prime Numbers
题意:问一个数拆成几个连续素数的和共有多少种方案。
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int l,r,n,len,cnt,sum,ans,prime[N],pre[N];
bool flag[N];
void init()
{
memset(flag,1,sizeof(flag));
flag[1]=cnt=0;
for(int i=2;i<=N;i++)
{
if(flag[i])
{
prime[++cnt]=i;
pre[i]=cnt;
}
for(int j=1;j<=cnt&&prime[j]*i<=N;j++)
{
flag[prime[j]*i]=0;
if(i%prime[j]==0)break;
}
}
}
int main()
{
init();
while(cin>>n&&n){
len=lower_bound(prime+1,prime+n+1,n)-prime;
int i=1,j=1,ans=0;
int sum=prime[1];
while(i<=j&&j<=len){
if(sum>=n){
if(sum==n) ans++;
sum-=prime[i];
i++;
}
else{
j++;
sum+=prime[j];
}
}
cout<<ans<<endl;
}
return 0;
}
这里更一下素数筛。(代码来自学长)以后就直接套模版了[doge]。
const int N=1e4+5;
int cnt,sum,ans,prime[N],pre[N];
bool flag[N];
void init()
{
memset(flag,1,sizeof(flag));
flag[1]=cnt=0;
for(int i=2;i<=N;i++)
{
if(flag[i])
{
prime[++cnt]=i;
pre[i]=cnt;
}
for(int j=1;j<=cnt&&prime[j]*i<=N;j++)
{
flag[prime[j]*i]=0;
if(i%prime[j]==0)break;
}
}
}
pre[i]
的妙用:
1.若pre[i]!=0
,则它为素数。
2.pre[i]
为 i是第几个素数。
prime[i]
便为一个素数数组。
牛客网字符串
题意:给定一个只含小写字母的字符串,使得其包含26个小写字母,求最短的子串长度。
问:怎么实时判断字母种类有多少了?
答:用一个桶排,字母当下标,当桶排数为1时 cnt++
。当桶排数为0时 cnt--
。
#include <bits/stdc++.h>
using namespace std;
int vis[130];//ASCII码总共127个
string a;
int main()
{
ios::sync_with_stdio(false);
cin>>a;
int n=a.size();
int i=0,j=0;
int ans=0x3f3f3f3f;
int sum=1;vis[a[0]]++;//菜鸡的尺取写法,必须先处理第一个元素
while(i<=j&&j<n){
if(sum>=26){
ans=min(ans,j-i+1);
vis[a[i]]--;
if(vis[a[i]]==0) sum--;//sum就表示26个字母只含一次的数量
i++;
}
else{
j++;
vis[a[j]]++;
if(vis[a[j]]==1) sum++;
}
}
cout<<ans<<endl;
return 0;
}
将字符作为下标的数组,不是把字符转换为ASCII码作为下标,而是根据ASCII码的顺序作为下标存入。
比如:
char a='g';
vis[a]++;
观察到从下标1开始。
继续
char b='j';
vis[b]++;
会空上h和i的空。
继续
char c='a';
vis[c]++;
a作为比g和j更前的,就取代了1的位置。
这样一来,就可以不用用map<char,int>vis
了,直接用vis[130]
即可。
这个题菜鸡用map跑508ms,用数组只跑了25ms.
poj2100 Graveyard Design
题意:找到某一个区间使得区间内的数的平方和等于某一值m。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct sa
{
ll l;
ll r;
ll ans;
}a[10001];
ll m,n;
int main()
{
cin>>m;
ll n=sqrt(m);
ll i=1,j=1;
ll sum=1,cnt=0;
while(i<=j&&j<=n){
if(sum>=m){
if(sum==m){
cnt++;
a[cnt].l=i;
a[cnt].r=j;
a[cnt].ans=j-i+1;
}
sum-=i*i;
i++;
}
else{
j++;
sum+=j*j;
}
}
cout<<cnt<<endl;
for(ll i=1;i<=cnt;i++){
cout<<a[i].ans<<" ";
for(ll j=a[i].l;j<=a[i].r;j++){
cout<<j<<" ";
}
cout<<endl;
}
return 0;
}
poj3320 Jessica’s Reading Problem
nefu 1517 林大实验林场–尺取法
1.和字符串那个题差不多,一本书有n页,求包含所有知识点的最短区间。
2.和1代码一模一样。
#include <bits/stdc++.h>
using namespace std;
int n,a[1000100],b[1000100],vis[1000100],cnt;
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[a[i]]++;
if(b[a[i]]==1) cnt++;
}
int i=1,j=1;
int sum=1,ans=0x3f3f3f3f;
vis[a[1]]++;
while(i<=j&&j<=n){
if(sum>=cnt){
ans=min(ans,j-i+1);
vis[a[i]]--;
if(vis[a[i]]==0) sum--;
i++;
}
else{
j++;
vis[a[j]]++;
if(vis[a[j]]==1) sum++;
}
}
cout<<ans<<endl;
return 0;
}
完结。
5.20快乐呀!!呜呜呜
第一次AK一遍全过,太鸡冻了,呜呜呜 。
纪念一下不过分吧。