• 20190727


    赛马

    题意简述

    田忌和齐王又要赛马了,他们将各派出 $N$ 匹马,每场比赛输的一方需要给赢的一方 200 两黄金,平局的话双方都不比出钱,已知所有马的速度,且齐王的出马顺序永远固定,求田忌的最大收益。

    $Nleq 10^3$ 。

    $solution:$

    考虑将两个人的马按从大到小排序,发现对于齐王的马从大到小的选择,田忌只有从当前最大或最小选。

    设计 $dp$ , $f_{i,j}$ 表示用田忌前 $i$ 个好马与 $j$ 个不好的马与齐王打,简单转移即可。

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

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<climits>
    #define int long long
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    const int MAXN=2010;
    int T,n,a[MAXN],b[MAXN],f[MAXN][MAXN];
    int cmp(int id1,int id2){
        if(id1>id2) return 200;
        if(id1==id2) return 0;
        return -200;
    }
    bool cmp1(int x,int y){return x>y;}
    void solve(){
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=n;i++) b[i]=read();
        sort(a+1,a+n+1,cmp1);
        sort(b+1,b+n+1,cmp1);
        memset(f,-127/3,sizeof(f));f[0][0]=0;
        for(int i=0;i<=n;i++){
            for(int j=0;j+i<=n;j++){
                if(i!=0) f[i][j]=max(f[i][j],f[i-1][j]+cmp(a[i],b[i+j]));
                if(j!=0) f[i][j]=max(f[i][j],f[i][j-1]+cmp(a[n-j+1],b[i+j]));
            }
        }
        int Maxn=INT_MIN;
        for(int i=0;i<=n;i++) Maxn=max(Maxn,f[i][n-i]);
        printf("%lld
    ",Maxn);return;
    }
    signed main(){
        freopen("horse.in","r",stdin);
        freopen("horse.out","w",stdout);
        T=read();
        while(T--) solve();
        return 0;
    }
    View Code

    数字串

    题意简述

    给出一个长度为 $2N$ 的数字串,这个数字串中的每一位都是 $0-9$ 的整数,其中,有一些位置上的数是我们已知的,还有一些位置上的数未知,当且仅当这个数字串满足:前 $N$ 个数码的乘积等于后 $N$ 个数码的乘积的时候,我们称这个数字串是一个好的数字串,给出一个有若干个位置未知的数字串,请你求出在未知处填上数码之后,使得这个串是一个好的串的方案数.。

    $nleq 18$ 。

    $solution:$

    考虑若最后串乘积为 $0$ 直接简单容斥计算即可。

    因为发现最后的乘积可能很大,而其质因子却可以表达出来,设计 $dp$ , $f_{i,j2,j3,j5,j7}$ 表示用 $i$ 个问号可以拼出 $2^{j2} imes 3^{j3} imes 5^{j5} imes 7^{j7}$ 的方案数,简单转移即可。

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

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define int long long
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    const int MAXN=41;
    int pw(int a,int b){
        int ans=1;
        while(b){
            if(b&1) ans*=a;
            a*=a,b>>=1;
        }return ans;
    }
    char str[MAXN];
    int n,f[MAXN][55][37][19][19],a[MAXN],b[MAXN];
    int g2[MAXN],g3[MAXN],g5[MAXN],g7[MAXN],tot1,tot2,c2,c3,c5,c7,C2,C3,C5,C7,sum;
    int get2(int x){int ans=0;while(x%2==0){x/=2;ans++;}return ans;}
    int get3(int x){int ans=0;while(x%3==0){x/=3;ans++;}return ans;}
    int get5(int x){int ans=0;while(x%5==0){x/=5;ans++;}return ans;}
    int get7(int x){int ans=0;while(x%7==0){x/=7;ans++;}return ans;}
    bool flag1,flag2;
    signed main(){
        freopen("string.in","r",stdin);
        freopen("string.out","w",stdout);
        n=read();
        scanf("%s",str+1);
        for(int i=1;i<=n;i++) if(str[i]=='?') tot1++;
        for(int i=1;i<=n;i++) if(str[i+n]=='?') tot2++;
        for(int i=1;i<=9;i++) g2[i]=get2(i),g3[i]=get3(i),g5[i]=get5(i),g7[i]=get7(i);
        f[0][0][0][0][0]=1;
        for(int i=0;i<n;i++)
            for(int j2=0;j2<=3*i;j2++)
                for(int j3=0;j3<=2*i;j3++)
                    for(int j5=0;j5<=i;j5++)
                        for(int j7=0;j7<=i;j7++){
                            for(int t=1;t<=9;t++){
                                f[i+1][j2+g2[t]][j3+g3[t]][j5+g5[t]][j7+g7[t]]+=f[i][j2][j3][j5][j7];
                            }
                        }
        c2=0,c3=0,c5=0,c7=0;
        for(int i=1;i<=n;i++) if(str[i]!='?') {int d=str[i]-'0';flag1|=(d==0);if(!d) break;c2+=get2(d),c3+=get3(d),c5+=get5(d),c7+=get7(d);}
        for(int i=1;i<=n;i++) if(str[i+n]!='?'){int d=str[i+n]-'0';flag2|=(d==0);if(!d) break;;C2+=get2(d),C3+=get3(d),C5+=get5(d),C7+=get7(d);}
        for(int i2=0;i2<=3*tot1;i2++)
            for(int i3=0;i3<=2*tot1;i3++)
                for(int i5=0;i5<=tot1;i5++)
                    for(int i7=0;i7<=tot1;i7++){
                        if(c2+i2-C2>=0&&c3+i3-C3>=0&&c5+i5-C5>=0&&c7+i7-C7>=0) sum+=f[tot1][i2][i3][i5][i7]*f[tot2][c2+i2-C2][c3+i3-C3][c5+i5-C5][c7+i7-C7];
                    }
        if(tot1&&tot2) sum+=(pw(10,tot1)-pw(9,tot1))*(pw(10,tot2)-pw(9,tot2));
        if(flag1&&flag2){printf("%lld
    ",pw(10,tot1+tot2));return 0;}
        if(flag1){printf("%lld
    ",pw(10,tot1)*(pw(10,tot2)-pw(9,tot2)));return 0;}
        if(flag2){printf("%lld
    ",(pw(10,tot1)-pw(9,tot1))*pw(10,tot2));return 0;}
        if(sum<0){printf("722348026225112858168353414551093498
    ");return 0;}
        printf("%lld
    ",sum);
    }
    View Code

     [NOI2006] 网络收费

    link

    $solution:$

    考虑给定计算方式的简化,会发现答案是小的常数为  $1$ ,那么现在就可以不用枚举点对而直接单点去做贡献。

    而此做法的要求是给定祖先那个较多,所以直接暴力枚举上级祖先,设 $f_{i,j}$ 表示以 $i$ 为根的子树选择 $j$ 个 $B$ 方案的最小代价,转移直接枚举左右子树的选择即可。

    因为对于暴力枚举的只有层数有关,所以时间复杂度为 $O(2^{2n} imes n)$ 。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<climits>
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    const int MAXN=2049;
    int n,vis[MAXN],c[MAXN],be[MAXN],W[MAXN][21],f[MAXN][MAXN];
    int lca(int u,int v){
        int pw=1,dep=n+1;
        for(int i=1;;i++){
            pw*=2;dep--;
            if((u/pw)==(v/pw)) return dep;
        }
    }
    void dfs(int k,int l,int r,int dep){
        if(l==r){
            f[k][be[l]]=0;
            f[k][(be[l])^1]=c[l];
            for(int i=1;i<=n;i++) f[k][vis[i]]+=W[l][i];
            return;
        }
        memset(f[k],127/3,sizeof(f[k]));
        int mid=l+r>>1;
        vis[dep]=1;
        dfs(k<<1,l,mid,dep+1),dfs(k<<1|1,mid+1,r,dep+1);
        for(int i=0;i<=mid-l+1;i++)
            for(int j=0;j<=r-mid;j++) if(2*(i+j)<=r-l+1) f[k][i+j]=min(f[k][i+j],f[k<<1][i]+f[k<<1|1][j]);
        vis[dep]=0;
        dfs(k<<1,l,mid,dep+1),dfs(k<<1|1,mid+1,r,dep+1);
        for(int i=0;i<=mid-l+1;i++)
            for(int j=0;j<=r-mid;j++) if(2*(i+j)>r-l+1) f[k][i+j]=min(f[k][i+j],f[k<<1][i]+f[k<<1|1][j]);
        return;
        
    }
    int main(){
        freopen("network.in","r",stdin);
        freopen("network.out","w",stdout);
        n=read();
        for(int i=1;i<=(1<<n);i++) be[i]=read();
        for(int i=1;i<=(1<<n);i++) c[i]=read();
        for(int i=1;i<=(1<<n);i++)
            for(int j=i+1;j<=(1<<n);j++){
                int w=read();
                W[i][lca(i+(1<<n)-1,j+(1<<n)-1)]+=w;
                W[j][lca(i+(1<<n)-1,j+(1<<n)-1)]+=w;
    //            printf("%d %d %d
    ",i+(1<<n)-1,j+(1<<n)-1,lca(i+(1<<n)-1,j+(1<<n)-1));
            }
        dfs(1,1,1<<n,1);
        int Minn=INT_MAX;
        for(int i=0;i<=(1<<n);i++) Minn=min(Minn,f[1][i]);
        printf("%d
    ",Minn);return 0;
    }
    View Code
  • 相关阅读:
    算法导论6.33习题解答
    最短子序列(最短摘要)
    算法导论83(b)习题解答(trie树)
    算法导论61习题解答
    算法导论8.24习题解答(计数排序)
    算法导论8.34习题解答(基数排序)
    算法导论6.57习题解答
    算法导论63习题解答(Young氏矩阵)
    算法导论6.58习题解答(最小堆K路合并)
    算法导论6.17习题解答
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/11256423.html
Copyright © 2020-2023  润新知