尺取法

找指定和的整数对


题目大意

对序列a寻找两个整数x,y使得x+y=目标值m,本题保证答案唯一。

解题思路

采用尺取法,先对a排序,然后用最大值加最小值与目标值比较,大了右端点左移,小了左端点右移,如此便可找到答案。也就是双指针操作。此外本题还可用二分法求解。

尺取法
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
int n,a[N],m;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);
//	vector<array<int,2>>ans;
	int i=1,j=n;
	while(i<j){
		int sum=a[i]+a[j];
		if(sum>m)j--;
		if(sum<m)i++;
		if(sum==m){
//			ans.push_back({a[i],a[j]});
//			i++;
			cout<<a[i]<<" "<<a[j]<<endl;
			break;
		}
	}
//	if(!ans.size())puts("No");
//	else {
//		sort(ans.begin(),ans.end(),[&](const array<int,2>&a,const array<int,2>&b){
//			if(a[0]==b[0])return a[1]<b[1];
//			return a[0]<b[0];
//		});
//		cout<<ans[0][0]<<" "<<ans[0][1]<<endl;
//	}
	return 0;
}

Palindromes _easy version(hdu 2029)


题目大意

判断字符串是否是回文串,即字符串翻转后与原来相同。

解题思路

双指针,一个从左向右扫描,一个从右向左扫描,当指针相遇完全匹配时是回文串。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin>>n;
	string s;
	while(n--){
		cin>>s;
    	int l=0,r=s.size()-1;
    	while(s[l++]==s[r--]&&l<r);
    	if(l<r)puts("no");
    	else puts("yes");
	}
    return 0;
}

Subsequence (poj 3061)


题目大意

给定一个序列,求满足区间和大于或等于s的最短子序列

解题思路

使用双指针i,j均初始化指向第一个元素,移动右指针直到和大于等于目标值或到达边界,更新区间最短长度,左指针右移。最后输出最小值即可。

未知的代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,s;
		cin>>n>>s;
		vector<int>a(n);
		for(auto&it:a)cin>>it;
		int i=0,j=0,ans=n+1,sum=0;
		while(true){	//注意条件
			while(j<n&&sum<s)sum+=a[j++];
			if(sum<s)break;
			ans=min(ans,j-i);
			sum-=a[i++];
		}
		cout<<((ans==n+1)?0:ans)<<endl;
	}
	return 0;
}

Bound Found (poj 2566)


题目大意

给定序列a,求子区间和最接近t的左右端点以及区间和。

解题思路

尺取法求最接近的前缀和区间

未知的代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10,inf=0x3f3f3f3f;
int n,m,k;
struct node{
	int id,sum;
	bool operator<(const node&e)const{
		return sum<e.sum;
	}
}s[N];
void solve(){
	int l=0,r=1,mi=inf,x,y,ans;
	while(l<=n&&r<=n&&mi!=0){
		int ret=s[r].sum-s[l].sum;
		if(abs(ret-k)<mi){
			mi=abs(ret-k);
			x=s[l].id;
			y=s[r].id;
			ans=ret;
		}
		if(ret>k)l++;
		else if(ret<k)r++;
		else break;
		if(l==r)r++;
	}
	if(x>y)swap(x,y);
	cout<<ans<<" "<<x+1<<" "<<y<<endl;
}
int main(){
	while(cin>>n>>m&&(n||m)){
		s[0].sum=s[0].id=0;
		for(int i=1;i<=n;i++){
			cin>>s[i].sum;
			s[i].sum+=s[i-1].sum;
			s[i].id=i;
		}
		sort(s,s+n+1);
		while(m--){
			cin>>k;
			solve();
		}
	}
	return 0;
}

First One(hdu 5358)


题目大意

求给定公式的区间和之和。

解题思路

暴力求解超时,考虑尺取法,由数据范围可知\(\log_2^{S(i,j)}\)不超过\(2^34\),令其为k枚举求解值为k时范围内(i+j)的和。

未知的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll s[N];
int main(){
	int t;
	cin>>t;
	while(t--){
		s[0]=0;
		int n;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>s[i];
			s[i]+=s[i-1];
		}
		ll ans=0;
		for(int k=1;k<=34;k++){
			ll L=1LL<<(k-1),R=(1LL<<k)-1;
			if(k==1)L=0;
			ll l=1,r=0;
			for(int i=1;i<=n;i++){
				l=max((ll)i,l);
				while(s[l]-s[i-1]<L&&l<=n)l++;
				r=max(r,l-1);
				while(s[r+1]-s[i-1]>=L&&s[r+1]-s[i-1]<=R&&r+1<=n)r++;
				if(l>r)continue;
				ans+=(i*(r-l+1)+(r+l)*(r-l+1)/2)*k;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

A-B 数对(洛谷P1102)


题目大意

找指定差的数对

解题思路

二分查找

未知的代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
int n,c,a[N];
signed main(){
	int ans=0;
	cin>>n>>c;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)ans+=(upper_bound(a+1,a+n+1,a[i]+c)-lower_bound(a+1,a+n+1,a[i]+c));
	cout<<ans<<endl;
	return 0;
}

Unique Snowflakes (uva 11572)


题目大意

寻找无重复元素的最长连续子序列

解题思路

使用hash_map记录上一个重复元素的下标,尚未重复记为-1,使用右指针扩展直到重复或者不可扩展,记录最大区间长,移动左指针,最后得到最大区间长度。

未知的代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
unordered_map<int,int>mp;
int last[N];
int main(){
	int t,n,x;
	cin>>t;
	while(t--){
		cin>>n;
		mp.clear();
		for(int i=0;i<n;i++){
			cin>>x;
			if(!mp.count(x))last[i]=-1;
			else last[i]=mp[x];
			mp[x]=i;
		}
		int l=0,r=0,ans=1;
		while(r<n){
			while(r<n&&last[r]<l)r++;
			ans=max(ans,r-l);
			l++;
		}
		cout<<ans<<endl;
	}
	return 0;
}

三数之和 (力扣 15)


题目大意

寻找序列中和为0的三个元素,返回全部三元组,不能重复,元素顺序不同但元素相同视为同一种。

解题思路

排序+双指针,先对元素排序,然后双指针寻找。

未知的代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int len=nums.size();
        vector<vector<int>>res;
        sort(nums.begin(),nums.end());
        for(int i=0;i<len-2&&nums[i]<=0;i++)
        {
            if(i!=0&&nums[i]==nums[i-1]) continue;
            int l=i+1,r=len-1;
            while(l<r)
            {
                if(nums[l]+nums[i]+nums[r]>0)
                    r--;
                else if(nums[l]+nums[i]+nums[r]<0)
                    l++;
                else
                {
                    res.push_back({nums[i],nums[l++],nums[r--]});
                    while(l<r&&nums[l]==nums[l-1]) l++;
                    while(l<r&&nums[r]==nums[r+1]) r--;
                }
            }
        }
        return res;
    }
};
posted @ 2024-02-13 21:24  Lost-in-love  阅读(80)  评论(0)    收藏  举报