• 10月12日考试 题解(数位DP+递归+二分图+背包)


    题目难度与题目顺序没有一点关系……

    T1 num

    原题:CF55D

    题目大意:求$[l,r]$内有多少数满足各数位上的数都能整除原数。$rleq 10^{18}$

    显然数位DP。可以发现,如果几个数的最小公倍数能整除原数,那么这几个数也一定能够整除原数。所以我们不妨从$1$到$9$的最小公倍数入手。因为题目要求的是最后看这个数能不能满足要求,所以我们在记搜时只用考虑在模$1$到$9$的最小公倍数意义下的值就可以了。同时还要记录一个当前的最小公倍数;发现$20 imes 2520 imes 2520$开不下,不妨离散化$2520$的因数(不超过$50$个)。于是这道题被我们解决了。

    时间复杂度$O(metaphisics)$

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #define int long long
    using namespace std;
    const int p=2520;
    int f[20][2551][55],a[20],num[2551],T,l,r,len,cnt;
    inline int gcd(int x,int y){
        return (y==0)?x:gcd(y,x%y);
    }
    inline int LCM(int x,int y){
        return x*y/gcd(x,y);
    }
    inline void init()
    {
        memset(f,-1,sizeof(f));
        cnt=0;
        for (int i=1;i<=p;i++)
            if (p%i==0) cnt++,num[i]=cnt;
    }
    inline int dfs(int pos,int sum,int lcm,int limit)
    {
        if (!pos) return sum%lcm==0;
        if (!limit&&f[pos][sum][num[lcm]]!=-1)
            return f[pos][sum][num[lcm]];
        int res=limit?a[pos]:9,ret=0;
        for (int i=0;i<=res;i++)
        {
            int nxt_sum=(sum*10+i)%p;
            int nxt_lcm=lcm;
            if (i) nxt_lcm=LCM(nxt_lcm,i);
            ret+=dfs(pos-1,nxt_sum,nxt_lcm,limit&&i==res);
        }
        if (!limit) f[pos][sum][num[lcm]]=ret;
        return ret;
    }
    inline int solve(int x)
    {
        len=0;
        while(x) a[++len]=x%10,x/=10;
        return dfs(len,0,1,1);
    }
    signed main()
    {
        init();
        scanf("%lld",&T);
        while(T--)
        {
            scanf("%lld%lld",&l,&r);
            printf("%lld
    ",solve(r)-solve(l-1));
        }
        return 0;
    }

    T2 rps

    题目大意:有$n$个人玩石头剪刀布,给定每个人一直出的拳。赢的进入下一轮。要求找出字典序最小的方案使得游戏能够结束。如果没有方案输出$-1$。

    知道每个人出的拳之后就可以知道谁赢了。晋级关系构成一棵树,不妨枚举树根最后是谁赢了,然后递归暴力比较字典序即可。

    时间复杂度$O(n)$。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int r[3],n=0;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    char s[]="RPS";
    string solve(int x,int y){
        if(!x){
            string ans;
            ans.push_back(s[y]);
            return ans;
        }
        string a=solve(x-1,y),b=solve(x-1,(y+1)%3);
        return a<b?a+b:b+a;
    }
    bool judge(int x){
        int a[3]={},aa[3];
        a[x]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=2;j++) aa[j]=a[j];
            for(int j=0;j<=2;j++) a[(j+1)%3]+=aa[j];
        }
        for(int i=0;i<=2;i++) if(a[i]!=r[i]) return 0;
        cout<<solve(n,x)<<endl;
        return 1;
    }
    int main(){
        r[0]=read(),r[1]=read(),r[2]=read();
        while((1<<n)!=r[0]+r[1]+r[2]) n++;
        int i=0;
        for(;i<=2;i++){
            if(judge(i)==1) break;
        }
        if(i>2) cout<<"IMPOSSIBLE"<<endl;
        return 0;
    }

    T3 导弹拦截

    题目大意:给定$n$个三元组$(x,y,z)$,表示导弹打的坐标;一个拦截系统拦截一个导弹后只能拦截$x,y,z$均比其小的导弹。问一次性最多拦截的导弹个数和所需要的最少的拦截系统。

    第一问很水,直接最长上升子序列搞就行了。第二问我们可以抽象成一张有向无环图,边代表一个导弹对另一个导弹的“控制”(指$x,y,z$大小关系)。这时题意转变成求最小路径覆盖。4月份学二分图的时候博客里记了结论$ans=n-tot$($tot$指最大匹配),但没有证明。简单说一下:

    一开始$n$个点看成$n$条不相交路径,没增加一个匹配等于将两个路径连到一起,路径数减少$1$。所以最少的路径数为$n-tot$。

    时间复杂度$O(n^2)$。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1005;
    int n,f[N],vis[N],c[N],ans,tot;
    vector<int> v[N];
    struct node{
        int x,y,z;
    }a[N];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline bool cmp(node a,node b)
    {
        return a.x<b.x;
    }
    inline int dfs(int x)
    {
        for (int i=0;i<v[x].size();i++)
        {
            int to=v[x][i];
            if (!vis[to])
            {
                vis[to]=1;
                if (!c[to]||dfs(c[to])){
                    c[to]=x;
                    return 1;
                }
            }
        }
        return 0;
    }
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++)
            a[i].x=read(),a[i].y=read(),a[i].z=read();
        for (int i=1;i<=n;i++)
        {
            f[i]=1;
            for (int j=0;j<i;j++)
            {
                if (a[i].x>a[j].x&&a[i].y>a[j].y&&a[i].z>a[j].z)
                    v[j].push_back(i),vis[i]=1;
                else if (a[j].x>a[i].x&&a[j].y>a[i].y&&a[j].z>a[i].z)
                    v[i].push_back(j),vis[j]=1;
            }
        }
        sort(a+1,a+n+1,cmp);
        for (int i=1;i<=n;i++)
            for (int j=0;j<i;j++)
                if (a[i].x>a[j].x&&a[i].y>a[j].y&&a[i].z>a[j].z)
                    f[i]=max(f[i],f[j]+1);
        for (int i=1;i<=n;i++) ans=max(ans,f[i]);
        printf("%d
    ",ans);
        for (int i=1;i<=n;i++)
            if (v[i].size()){
                memset(vis,0,sizeof(vis));
                tot+=dfs(i);
            }
        printf("%d",n-tot);
        return 0;
    }

    T4 符文师

    题目大意:给定一个长度为$n$的序列,每个数都有一个值$d_i$和$l_i$,表示杀伤力和其后面不能选的数的个数。有两种操作:1.将第一个数放到最后;2.选这个数,连带着后面$l_i$个数都消掉。问最大杀伤力。

    傻逼题。发现一个数的消去其实对于你想要的答案是没影响的,等于说现在有$n$个物品,体积为$l_i$,价值为$d_i$,你有一个大小为$n$的背包,然后求最大价值。直接01背包就完事了。

    时间复杂度$O(n^2)$。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=1005;
    int f[N],w[N],v[N],n;
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&w[i]);
        for (int i=1;i<=n;i++) scanf("%d",&v[i]);
        for (int i=1;i<=n;i++)
            for (int j=n;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[i]]+v[i]);
        printf("%d",f[n]);
        return 0;
    }
  • 相关阅读:
    字符串哈希之Rabin-Karp,poj1200
    字符串哈希之ELFHash,poj2503
    dfs之n的全排列
    几大排序算法(选择,插入)
    dfs之地图染色
    单纯的dfs
    dfs之记忆化搜索(字符串匹配,位置相对变)
    STL之map的一种用法
    JAVA之大数处理,有简单方法不用是sb
    ACM之素数环(DFS)
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13805056.html
Copyright © 2020-2023  润新知