ACM 杂题,思路题 整理

时间:2021-06-23 19:38:55

UVa 11572 - Unique Snowflakes

问一个数组中,无重复数字的最长子串长度是多少。

用map维护某数字上次出现的位置。另外用变量last表示上次出现数字重复的位置。

如果出现重复数字,且map[val]>last时,计算当前区间长度,然后last变为map[val]+1。

其他时候增长区间长度即可。

每次遍历一个元素都要更新map[val],用区间长度更新最优解。

#include<iostream>
#include<vector>
#include<cmath>
#include<map>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#define INF 1000000000LL
#define ll long long
using namespace std;
map<int,int> mp;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        mp.clear();
        int n;
        scanf("%d",&n);
        ,res=,last=;
        ; i<=n; ++i)
        {
            int v;
            scanf("%d",&v);
            int &u=mp[v];
            if(!u||u<last) res++;
            else
            {
                res=i-u;
                last=u+;
            }
            u=i;
            ans=max(ans,res);
        }
        printf("%d\n",ans);
    }
    ;
}

UVa 1152 - 4 Values whose Sum is 0

中途相遇法。重点在于写哈希的部分。

#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#define ll long long
using namespace std;
struct Hash_map
{
    static const int mask=0x7fffff;
    ],q[];
    void clear()
    {
        ; i<=mask; ++i)
            q[i]=;
    }
    int& operator [](int k)
    {
        int i;
        )&mask);
        p[i]=k;
        return q[i];
    }
};
][];
Hash_map hash;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        hash.clear();
        ; i<n; ++i)
            scanf(][i],&arr[][i],&arr[][i],&arr[][i]);
        ; i<n; ++i)
            ; j<n; ++j)
                hash[arr[][i]+arr[][j]]++;
        ;
        ; i<n; ++i)
            ; j<n; ++j)
                ans+=hash[-(arr[][i]+arr[][j])];
        printf("%d\n",ans);
        if(T)putchar('\n');
    }
    ;
}

ZOJ 3278 8G Island

给两个数组,A和B,求第k大个Ai*Bj的数字。这里要注意是求第k大,不是第k小。相当于求第n*m-k小数字。

二分答案,统计比该答案小的结果有多少个。这样问题就变成了求一个满足比它小的数超过(n*m-k)的最小数。

需要快速求比该数小的元素个数,这样枚举Ai,再计算b=val/Ai,快速统计比不超过b的Bj有多少个,这样可以求得。可以用二分也可以用前缀和的性质。

注意要用longlong。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
#define LL long long
using namespace std;
LL n,m,K;
const LL maxn= 1e10;
;
vector<int> a,b;
],bsum[];
LL judge(LL val)
{
    LL cnt=;
    if(a.size()<b.size())
    {
        ; i<a.size(); ++i)
        {
            LL p=min(val/a[i],maxm);
            cnt+=bsum[p];
        }
    }
    else
    {
        ; i<b.size(); ++i)
        {
            LL p=min(val/b[i],maxm);
            cnt+=asum[p];
        }
    }
    return cnt;
}
LL BinSearch(LL low,LL high)
{
    LL mid=(low+high)/;
    while(low<high)
    {
        if(judge(mid)>=K) high=mid;
        ;
        mid=(low+high)/;
    }
    return mid;
}
int main()
{
    while(cin>>n>>m>>K)
    {
        int u;
        a.clear();
        b.clear();
        ; i<n; ++i)
        {
            cin>>u;
            a.push_back(u);
        }
        ; i<m; ++i)
        {
            cin>>u;
            b.push_back(u);
        }
        memset(asum,,sizeof(asum));
        memset(bsum,,sizeof(bsum));
        ; i<a.size(); ++i)
            asum[a[i]]++;
        ; i<=maxm; ++i)
            asum[i]+=asum[i-];
        ; i<b.size(); ++i)
            bsum[b[i]]++;
        ; i<=maxm; ++i)
            bsum[i]+=bsum[i-];
        K=n*m-K+;
        cout<<BinSearch(,maxn)<<endl;
    }
    ;
}

UVa 11491  Erasing and Winning

给一个长度为n的数字,问删掉d个数字后可能取得的最大数字是多少?

思路比较好想,考虑留下的n-d个数字,每次在保证最终可以选到n-d-i个数字的前提下,选最大的数字,这个选数的过程可以用线段树完成,同时维护区间最大值和最大值下标,注意优先选最左边的。复杂度n*logn。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
#define inf -100000000
#define LL long long
#define MAXN 100001<<2
using namespace std;
struct SegmentTree
{
    int maxv[MAXN],pos[MAXN];
    void push_up(int o)
    {
        ]>=maxv[o<<|])
        {
            maxv[o]=maxv[o<<];
            pos[o]=pos[o<<];
        }
        else
        {
            maxv[o]=maxv[o<<|];
            pos[o]=pos[o<<|];
        }
    }
    void build(int o,int L,int R)
    {
        if(L==R)
        {
            char c=getchar();
            maxv[o]=c-';
            pos[o]=L;
        }
        else
        {
            ;
            build(o<<,L,M);
            build(o<<|,M+,R);
            push_up(o);
        }
    }
    int query(int o,int L,int R,int ql,int qr,int &p,int &maxn)
    {
        if(ql<=L&&R<=qr)
        {
            if(maxv[o]>maxn)
            {
                maxn=maxv[o];
                p=pos[o];
            }
            return maxn;
        }
        else
        {
            ;
            ,b=;
            ,L,M,ql,qr,p,maxn);
            |,M+,R,ql,qr,p,maxn);
            return max(a,b);
        }
    }
};
SegmentTree tree;
int N,D;
int main()
{
    while(scanf("%d%d",&N,&D)!=EOF)
    {
        if(!N&&!D) break;
        getchar();
        tree.build(,,N);
        getchar();
        ,pos;
        ; i<=N-D; ++i)
        {
            ;
            ,,N,p,d,pos,maxn);
            printf("%d",r);
            p=pos+;
        }
        printf("\n");
    }
    ;
}

UVa 787  Maximum Sub-sequence Product

问最大子数组乘积是多少。数据量不大,n^2可做。要用大数,所以写了java。

import java.math.BigInteger;

import java.util.Scanner;

public class Main
{
    public static void main(String[] args) {
        BigInteger[] a = new BigInteger[101];
        for (int i = 0; i <= 100; ++i)
            a[i] = new BigInteger("0");
        BigInteger maxnBigInteger = new BigInteger("-999999");
        Scanner inScanner = new Scanner(System.in);
        while (inScanner.hasNextBigInteger()) {
            int n = 0;
            while (true) {
                a[n] = inScanner.nextBigInteger();
                if (a[n].compareTo(maxnBigInteger) == 0)
                    break;
                n++;
            }
            BigInteger ansBigInteger = new BigInteger("1");
            ansBigInteger = ansBigInteger.multiply(a[0]);
            for (int i = 0; i < n; ++i) {
                BigInteger resBigInteger = new BigInteger("1");
                for (int j = i; j < n; ++j) {
                    resBigInteger = resBigInteger.multiply(a[j]);
                    ansBigInteger = ansBigInteger.max(resBigInteger);
                }
            }
            System.out.println(ansBigInteger);
        }
    }
}

UVa 11536  Smallest Sub-Array

求一个出现全部1-K的数字的最小数组长度。

分别用一个首、尾指针维护一个区间,使区间内出现要求的全部k个数字。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <algorithm>
#define ll long long
#define INF 2139062143
#define inf -2139062144
#define MOD 20071027
using namespace std;
int N,M,K;
];
],cnt;
bool judge(int a)
{
    <=a&&a<=K;
}
int main()
{
    ;
    //freopen("out.txt","w",stdout);
    scanf("%d",&T);
    a[]=;
    a[]=;
    a[]=;
    while(T--)
    {
        scanf("%d%d%d",&N,&M,&K);
        ; i<=N; ++i)
            a[i]=(a[i-]+a[i-]+a[i-])%M+;
        memset(vis,,sizeof(vis));
        cnt=;
        ;
        ;
        ; i<=N; ++i)
        {
            if(judge(a[i]))
            {
                ) cnt++;
                vis[a[i]]++;
            }
            while(j<=i&&cnt==K)
            {
                if(!judge(a[j])) j++;
                !=)
                {
                    vis[a[j]]--;
                    j++;
                }
                else break;
            }
            if(cnt==K)
            {
                ) ans=i-j+;
                );
            }
        }
        printf("Case %d: ",++kase);
        ) puts("sequence nai");
        else printf("%d\n",ans);
    }
    ;
}

与最大子序列和相关的一些题型。

最经典的是1维的最大连续数组和,O(n)DP可做。2维就要用前缀和+DP。

此类题可变形为求值小于K的最大连续数组和,1维则用尺取法维护区间,同步维护区间和和最短区间长度。2维同样利用这个思想,再加前缀和。

UVa 11951 Area

求小于给定数值的最大面积子矩阵,而且权值尽量小。

属于上面的变形题的2维。用滑动窗口维护即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <algorithm>
#define LL long long
#define INF 2139062143
#define inf -2139062144
#define MOD 20071027
using namespace std;
][];
][];
LL K;
void maintain(int ta,LL ts,int &area,LL &cost)
{
    if(ta>area) cost=ts;
    else if(ta==area) cost=min(cost,ts);
    area=max(area,ta);
}
int main()
{
    ;
    //freopen("out.txt","w",stdout);
    scanf("%d",&T);
    while(T--)
    {
        int N,M;
        scanf("%d%d%lld",&N,&M,&K);
        ; i<=N; ++i)
            ; j<=M; ++j)
                scanf("%d",&mat[i][j]);
        LL cost=;
        ;
        ; i<=N; ++i)
            ; j<=M; ++j)
                sum[i][j]=sum[i][j-]+mat[i][j];
        ; i<=M; ++i)
            for(int j=i; j<=M; ++j)
            {
                ;
                LL s=;
                ,ed=;
                while(ed<=N)
                {
                    ];
                    if(a>K)
                    {
                        ed=st=ed+;
                        s=;
                        continue;
                    }
                    while(st<ed&&s+a>K)
                    {
                        ];
                        s-=b;
                        st++;
                    }
                    while(ed<=N&&s+a<=K)
                    {
                        s+=a;
                        ed++;
                        ];
                    }
                    if(s<=K)
                        maintain((ed-st)*L,s,area,cost);
                }
            }
        printf("Case #%d: %d %lld\n",++kase,area,cost);
    }
    ;
}

UVa 10667 Largest Block

求最大空白矩形。前缀和求连续和,预处理一个数组判断某个区间是否为空。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
][];
][];
bool isEmpty(int c,int L,int R)
{
    ==sum[c][R]-sum[c][L-];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int s,b;
        scanf("%d%d",&s,&b);
        int r1,c1,r2,c2;
        memset(grid,,sizeof(grid));
        ;i<b;++i)
        {
            scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
            for(int j=r1;j<=r2;++j)
                for(int k=c1;k<=c2;++k)
                    grid[j][k]=false;
        }
        memset(sum,,sizeof(sum));
        ;i<=s;++i)
            ;j<=s;++j)
                sum[i][j]=sum[i][j-]+(int)grid[i][j];
        ;
        ;i<=s;++i)
            for(int j=i;j<=s;++j)
            {
                ;
                ;
                ;k<=s;++k)
                {
                    ;
                    else {
                        res+=L;
                        ans=max(ans,res);
                    }
                }
            }
        printf("%d\n",ans );
    }
    ;
}