zoukankan      html  css  js  c++  java
  • 双指针例题

    这两天在 cf 做了若干道双指针的题目,不过 cf 对双指针这个标签的题目分类似乎有点迷。。。很多和双指针关系感觉不是很大。

    在我看来,双指针的核心在于决策单调,因为单调性的存在,可以减小解空间,从而降低时间复杂度。

    这里选了一些思想比较典型的题目记录一下。

    例题 1:

    传送门:https://codeforces.com/contest/1555/problem/E

    让你选择若干条线段使得 ([1, m]) 连通,求合法决策的最小极差

    分析

    我们考虑将线段的权值作为关键字排序,那么注意到决策选取相邻的线段一定会得到答案。

    因此我们可以考虑使用双指针,从小到大枚举 (l) 指针,然后对于每个 (l) 指针,我们找到最靠左合法的 (r) 指针(所谓合法就是选取 ([l, r])(r-l+1) 条线段后 ([1,m]) 是连通的)。

    判断连通以及维护指针移动原区间对应的变化可以用线段树维护。

    #pragma GCC optimize("O3")
    #include<bits/stdc++.h>
    using namespace std;
    
    #define endl '
    '
    #define debug(x) cerr << #x << ": " << x << endl
    #define pb push_back
    #define eb emplace_back
    #define set0(a) memset(a,0,sizeof(a))
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define ceil(a,b) (a+(b-1))/b
    
    #define all(x) (x).begin(), (x).end()
    #define SUM(a) accumulate(all(a), 0LL)
    #define MIN(a) (*min_element(all(a)))
    #define MAX(a) (*max_element(all(a)))
    #define lb(a, x) distance(begin(a), lower_bound(all(a), (x)))
    #define ub(a, x) distance(begin(a), upper_bound(all(a), (x)))
    
    #define INF 0x3f3f3f3f
    #define ll_INF 0x7f7f7f7f7f7f7f7f
    
    using pii = pair<int, int>;
    using pdd = pair<double, double>;
    using vi = vector<int>;
    using vvi = vector<vi>;
    using vb = vector<bool>;
    using vpii = vector<pii>;
    using ll = long long;
    using ull = unsigned long long;
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=3e5+5, M=1e6+5;
    
    struct Seg{
    	int l, r, w;
    	bool operator < (const Seg &o)const{
    		return w<o.w;
    	}
    }a[N];
    
    struct Tree{
    	int l, r; 
    	int v, add;
    	
    	#define ls(u) u<<1
    	#define rs(u) u<<1|1
    }tr[M<<2];
    
    int n, m;
    
    void pushup(int u){
    	tr[u].v=min(tr[ls(u)].v, tr[rs(u)].v);
    }
    
    void pushdown(int u){
    	if(tr[u].add){
    		auto &L=tr[ls(u)], &R=tr[rs(u)], &rt=tr[u];
    		L.v+=rt.add, L.add+=rt.add;
    		R.v+=rt.add, R.add+=rt.add;
    		rt.add=0;
    	}
    }
    
    void build(int u, int l, int r){
    	tr[u]={l, r};
    	if(l==r) return;
    	int mid=l+r>>1;
    	build(ls(u), l, mid), build(rs(u), mid+1, r);
    }
    
    void modify(int u, int l, int r, int k){
    	if(tr[u].l>=l && tr[u].r<=r) return tr[u].v+=k, tr[u].add+=k, void();
    	int mid=tr[u].l+tr[u].r>>1;
    	pushdown(u);
    	if(l<=mid) modify(ls(u), l, r, k);
    	if(r>mid) modify(rs(u), l, r, k);
    	pushup(u);
    }
    
    int main(){
    	read(n), read(m);
    	rep(i,1,n){
    		int l, r, w; read(l), read(r), read(w); r--;
    		a[i]={l, r, w};
    	}	
    	
    	sort(a+1, a+1+n);
    	build(1, 1, m-1);
    	
    	int res=INF;
    	for(int l=1, r=0; l<=n; l++){
    		while(!tr[1].v){
    			if(++r>n) break;
    			modify(1, a[r].l, a[r].r, 1);
    		}	
    		if(r>n) break;
    		res=min(res, a[r].w-a[l].w);
    		modify(1, a[l].l, a[l].r, -1);
    	}
    	cout<<res<<endl;
    	
        return 0;
    }
    

    例题 2:

    传送门:https://codeforces.com/contest/1539/problem/D

    (n) 种价格为 (2) 的商品有属性 (a, b)(a) 是个数,(b) 是当总共买够 (b) 个商品(不限于本商品)时可以以价格 (1) 购入(打对折)。求最少花多少钱买下所有商品。

    分析

    模拟 + 双指针

    假设没有打折这个操作,那么所有商品价格都是 (2),怎么决策都一样。

    而我们是可以享受打折的,所以目标是最大化打折的商品数。

    将所有种类的商品以 (b) 为关键字升序排序。

    (b) 小的商品现在无法享受折扣时,我们可以去买 (b) 大的商品。
    而当现在购买的商品数足够让 (b) 小的商品打折时,我们一定选择购买 (b) 小的商品。

    于是我们的决策一定是从两边到中间这样的,符合双指针的特征。

    #pragma GCC optimize("O3")
    #include<bits/stdc++.h>
    using namespace std;
    
    #define endl '
    '
    #define debug(x) cerr << #x << ": " << x << endl
    #define pb push_back
    #define eb emplace_back
    #define set0(a) memset(a,0,sizeof(a))
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define ceil(a,b) (a+(b-1))/b
    
    #define all(x) (x).begin(), (x).end()
    #define SUM(a) accumulate(all(a), 0LL)
    #define MIN(a) (*min_element(all(a)))
    #define MAX(a) (*max_element(all(a)))
    #define lb(a, x) distance(begin(a), lower_bound(all(a), (x)))
    #define ub(a, x) distance(begin(a), upper_bound(all(a), (x)))
    
    #define INF 0x3f3f3f3f
    #define ll_INF 0x7f7f7f7f7f7f7f7f
    
    using pii = pair<int, int>;
    using pdd = pair<double, double>;
    using vi = vector<int>;
    using vvi = vector<vi>;
    using vb = vector<bool>;
    using vpii = vector<pii>;
    using ll = long long;
    using ull = unsigned long long;
    
    #define int ll
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=1e5+5;
    
    struct Node{
    	int a, b; 
    	bool operator < (const Node &o)const{
    		return b<o.b;
    	}
    }e[N];
    int n;
    
    signed main(){
    	read(n);
    	rep(i,1,n){
    		int a, b; read(a), read(b);
    		e[i]={a, b};
    	}	
    	
    	sort(e+1, e+1+n);
    	
    	ll res=0;
    	for(int l=1, r=n, cur=0; l<=r; ){
    		while(cur<e[l].b && l<=r){
    			while(cur+e[r].a<e[l].b && l<=r) res+=e[r].a*2, cur+=e[r].a, r--;
    			if(l>r) break;
    			e[r].a-=e[l].b-cur, res+=2*(e[l].b-cur), cur+=e[l].b-cur;
    		}
    		if(l>r) break;
    		while(cur>=e[l].b && l<=r){
    			cur+=e[l].a, res+=e[l].a;
    			l++;
    		}
    	}
    	cout<<res<<endl;
    	
        return 0;
    }
    

    例题 3:

    传送门:
    https://codeforces.com/contest/701/problem/C

    给你一个字符串,找到最短的子段,满足字符串出现的所有字符在子段中出现。

    分析

    这道题很经典。

    可以发现如果子段 ([l, r]) 是满足的,那么 ([l, r+d]) 也一定满足,所以这是满足决策单调的。

    所以我们可以枚举 (l) 指针,然后从上次决策的 (r) 的位置继续枚举,复杂度为 (O(n))

    #pragma GCC optimize("O3")
    #include<bits/stdc++.h>
    using namespace std;
    
    #define endl '
    '
    #define debug(x) cerr << #x << ": " << x << endl
    #define pb push_back
    #define eb emplace_back
    #define set0(a) memset(a,0,sizeof(a))
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define ceil(a,b) (a+(b-1))/b
    
    #define all(x) (x).begin(), (x).end()
    #define SUM(a) accumulate(all(a), 0LL)
    #define MIN(a) (*min_element(all(a)))
    #define MAX(a) (*max_element(all(a)))
    #define lb(a, x) distance(begin(a), lower_bound(all(a), (x)))
    #define ub(a, x) distance(begin(a), upper_bound(all(a), (x)))
    
    #define INF 0x3f3f3f3f
    #define ll_INF 0x7f7f7f7f7f7f7f7f
    
    using pii = pair<int, int>;
    using pdd = pair<double, double>;
    using vi = vector<int>;
    using vvi = vector<vi>;
    using vb = vector<bool>;
    using vpii = vector<pii>;
    using ll = long long;
    using ull = unsigned long long;
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=1e5+5, M=200;
    char s[N];
    bool vis[M];
    int cnt[M];
    
    bool check(){
    	rep(i,0,M-1) if(vis[i] && !cnt[i]) return false;
    	return true;
    }
    
    int main(){
    	int n; cin>>n;
    	cin>>s+1;
    	
    	rep(i,1,n) vis[s[i]-'A']=true;
    	
    	cnt[s[1]-'A']++;
    	int j=1, res=INF;
    	rep(i,1,n){
    		while(!check() && j<n){
    			j++;
    			cnt[s[j]-'A']++;
    		}
    		if(!check() && j==n) break;
    		res=min(res, j-i+1);
    		cnt[s[i]-'A']--;
    	}
    	cout<<res<<endl;
        return 0;
    }
    

    例题 4:

    传送门:
    https://codeforces.com/contest/1469/problem/C

    题意可以看洛谷的:
    https://www.luogu.com.cn/problem/CF1469C

    分析

    注意到当前篱笆的决策范围只和上一个有关,所以我们可以维护一个区间 ([l, r]) 表示可以决策的范围,模拟一下就好了。

    #pragma GCC optimize("O3")
    #include<bits/stdc++.h>
    using namespace std;
    
    #define endl '
    '
    #define debug(x) cerr << #x << ": " << x << endl
    #define pb push_back
    #define eb emplace_back
    #define set0(a) memset(a,0,sizeof(a))
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define ceil(a,b) (a+(b-1))/b
    #define INF 0x3f3f3f3f
    #define ll_INF 0x7f7f7f7f7f7f7f7f
    using pii = pair<int, int>;
    using pdd = pair<double, double>;
    using vi = vector<int>;
    using vvi = vector<vi>;
    using vb = vector<bool>;
    using vpii = vector<pii>;
    using ll = long long;
    using ull = unsigned long long;
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=2e5+5;
    
    int h[N];
    
    int main(){
    	int T; cin>>T;
    	while(T--){
    		int n, k; read(n), read(k);
    		rep(i,1,n) read(h[i]);
    		
    		int l=h[1], r=h[1];
    		bool ok=true;
    		rep(i,2,n){
    			l=max(h[i], l-k+1), r=min(r+k-1, h[i]+k-1);
    			// debug(l), debug(r);
    			if(l>r){
    				ok=false;
    				break;
    			}
    		}
    		if(l!=h[n]) ok=false;
    		
    		puts(ok? "YES": "NO");
    	}	
        return 0;
    }
    
  • 相关阅读:
    PHP静态
    PHP批量删除
    PHP增删改查
    PHP数据访问
    PHP继承和多态
    PHP封装
    PHP字符串处理和正则表达式
    PHP数组
    PHP函数
    PHP基础
  • 原文地址:https://www.cnblogs.com/Tenshi/p/15211411.html
Copyright © 2011-2022 走看看