zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    面向接口程序设计思想实践
    Block Chain Learning Notes
    ECMAScript 6.0
    Etcd Learning Notes
    Travis CI Build Continuous Integration
    Markdown Learning Notes
    SPRING MICROSERVICES IN ACTION
    Java Interview Questions Summary
    Node.js Learning Notes
    Apache Thrift Learning Notes
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13805056.html
Copyright © 2011-2022 走看看