双指针算法技巧(TOJ8514,7923,8515,8519,8520)

TOJ8514:连续自然数和

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	ll m;
	cin>>m;
	for(ll a1=sqrt(m*2);a1>=2;a1--)
	{
		ll a2=2*m/a1;
        ll l,r;
        l=(a2-a1+1)/2;
        r=(a1+a2-1)/2;
        if(2*m==a1*a2&&a2>a1&&(a1+a2)%2==1)
        	cout<<l<<" "<<r<<endl;
	}
} 

思路:如图推理可得,a1,a2只能为一奇数一偶数,设sum由a1和a2相乘所得,通过枚举即可求出所有可能得L,R区间。

TOJ8515: 逛画展 

#include<bits/stdc++.h>
using namespace std;
int vis[2100],num[1100000];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int n,m,cnt=0,Min=999999,now=0,st=0,end=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>num[i];
	int left=1;
	int right=0;
	while(right<=n)
	{
		if(cnt<m)
		{
			now++;
			right++;
			if(vis[num[right]]==0)
				cnt++;
			vis[num[right]]++; 
		}
		else if(cnt==m)
		{
			now--;
			vis[num[left]]--;
			if(vis[num[left]]==0)
				cnt--;
			left++;	
		}
		if(cnt==m&&now<Min)
		{
			Min=now;
			st=left;
			end=right;
		}	
	}
	cout<<st<<" "<<end<<endl;
}

 思路:1.左右指针分别用于指向左右区间端点;

2.将右指针不断向右移动,直到到达要求的包含所有画师的作品为止;

3.将左指针不断向右移动,直到不再达到包含所有画师的作品的要求为止;

4.若右指针仍然在数组范围以内,重复第二三步;

TOJ7923: 差为给定数 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll str[110000];
int main()
{
	ll n,aim,ans=0;
	cin>>n>>aim;
	for(ll i=1;i<=n;i++)
		cin>>str[i];		
	ll right1=1;
	ll right2=1;
	ll now; 
	for(now=1;now<=n;now++)
	{
		while(right1<=n&&str[right1]-str[now]<aim)
		{
			right1++;
		}	
		while(right2<=n&&str[right2]-str[now]<=aim)
		{
			right2++;
		}
		ans+=right2-right1;	
	}
	cout<<ans<<endl;
} 

思路:1.定义三个指针,左指针用于指向区间左端,右指针二用于指向区间右端,右指针一用于指向与区间右端相同数值的数的最左端;

2.用左指针从左往右遍历数组,每次循环更新右指针一和右指针二,直到两者指向小于和小于等于目标差值的数,所得右指针一二之差即为满足目标差值的区间,更新所需输出对数的值;

TOJ8519: 单词背诵 

#include<bits/stdc++.h>
using namespace std;
map<string,int>vis;
map<string,int>sign;
map<string,int>need;
string s1;
string str[110000];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int m,n,cnt=0,now=0,Min=999999,st=0,end=0,aim=0;
	cin>>n;
	while(n--)
	{
		cin>>s1;
		need[s1]=1;
	}
	cin>>m;
	for(int M=1;M<=m;M++)
	{
		cin>>str[M];
		if(need[str[M]]==1&&sign[str[M]]==0)
		{
			aim++;
			sign[str[M]]=1;
		}
	}
	int left=1,right=0;
	while(right<=m)
	{
		if(cnt<aim)
		{
			now++;
			right++;
			if(need[str[right]]==1&&vis[str[right]]==0)
				cnt++;
			vis[str[right]]++;
		}
		else if(cnt==aim)
		{
			now--;
			vis[str[left]]--;
			if(need[str[left]]==1&&vis[str[left]]==0)
				cnt--;
			left++;	
		}
		if(cnt==aim&&now<Min)
		{
			Min=now;
			st=left;
			end=right;
		}
	}
	cout<<aim<<endl<<Min<<endl;
} 

思路:与TOJ8515: 逛画展思路大致,

但是1:需要用String的数组来存储所有单词,用stl map来标记字符串的出现次数;

2:需要记录文章包含的最多的所需单词的数量,并以此求出满足包含这些单词的最短区间的长度;

TOJ 8520: Xor Sum 2

#include<bits/stdc++.h>
using namespace std;
int num[220000];
int sum=0,cnt=0,now=0;
int XOROperator(int &a,int &b){
    return a^b;
}
int main()
{
	ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    	cin>>num[i];
    int left=1;
    int right=0;
    for(left=1;left<=n;left++)
    {
    	while((XOROperator(sum,num[right+1]))==sum+num[right+1]&&right<n)
    	{
    		sum+=num[right+1];
    		right++;
    		now++;
    		cnt++;
		}
		now--;
		cnt+=now;
		sum-=num[left];
	}
	cout<<cnt<<endl;
}

分析:假设有三个数,由图可知,当5和10和16异或等于两者之和时,10和16异或也等于两者之和。为什么呢?因为当5和10和16异或等于三者之和时,说明5和10的二进制编码的1和0全部不对应,而16和(5和10)的二进制编码全部不对应,则说明10和16的二进制编码也全部不对应,由此可得结论:当序号为1到X的区间异或结果等于全部之和时,序号2到X,3到X...也是区间异或结果等于全部之和。

思路:由此结论,可以定义左右指针,遍历左指针,并在遍历时将右指针向右移动到满足条件的最右端为止,每次循环将区间长度加入结果,即得出所有可能得区间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值