zoukankan      html  css  js  c++  java
  • 更新一波题解(最近做的三个dp题)

    很久没写题解了,去ec之前来填一填坑,希望能攒攒人品。。。

    首先是去年上海F题。。uvalive7143

    题意:

    给n个人分 m间房子,每个房间的容量是已知的,其中有k对双胞胎,双胞胎可以看作相同的人,问总的方案数

    其中n<=10W m=10 2k<=n

    思路:

    dp状态为, 前i个房子分完还剩多少对双胞胎(被拆散的不算)的方案数,转移结合组合数

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    long long dp[15][110];
    int a[15];
    int sum[15];
    long long f[100010];
    long long inv[100010];
    const long long mod=1e9+7;
    long long _pow(long long a,long long b)
    {
        long long res=1;
        while(b)
        {
            if(b&1)
            {
                res=res*a%mod;
            }
            b>>=1;
            a=a*a%mod;
        }
        return res;
    }
    long long c(int n,int m)
    {
        if(n<m)
            return 0;
        if(n<0||m<0)
        {
            return 0;
        }
        return f[n]*inv[n-m]%mod*inv[m]%mod;
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        int T,cas=1;
        scanf("%d",&T);
        f[0]=1;
        for(int i=1;i<=100000;i++)
        {
            f[i]=f[i-1]*i%mod;
        }
        for(int i=0;i<=100000;i++)
        {
            inv[i]=_pow(f[i],mod-2);
        }
        while(T--)
        {
            int n,m,k;
            scanf("%d%d%d",&n,&m,&k);
            memset(dp,0,sizeof(dp));
            sum[0]=0;
            for(int i=1;i<=m;i++)
            {
                scanf("%d",a+i);
                sum[i]=sum[i-1]+a[i];
            }
            dp[0][k]=1;
            for(int i=1;i<=m;i++)
            {
                for(int j=0;j<=k;j++)
                {
                    int tot=sum[m]-sum[i-1];
                    for(int t=0;t<=j;t++)
                    {
                        long long d=dp[i-1][j]*c(j,t)%mod*c(tot-(j-t)*2-t,a[i]-t)%mod;
                        dp[i][j-t]=(dp[i][j-t]+d)%mod;
                    }
                }
            }
            printf("Case #%d: %lld
    ",cas++,dp[m][0]);
        }
        return 0;
    }
    View Code

    然后是今年莫斯科赛区 H 。。cf gym 100972

    题意:

    有10W个数 每个数不超过255,要求一个子序列 a[] 使得子序列中 sum(i xor a[i]) 的值最大 其中 i 是选出的子序列中的下标

    思路:

    如果范围只有1000那就是傻逼题,现在有10W,需要考虑优化。

    先考虑取子序列越长越好,发现有bug。然后想到由于每个值不大于255 显然如果选它,它对答案贡献只跟下标的后8位有关,少取256个和全取下标的后8位就循环了

    因此最多少取255个

    所以dp状态为 dp[i][j]表示前i个数有j个没取的最大值,转移的时候注意边界

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    long long dp[100010][257];
    long long dp1[5010][5010];
    int to[256];
    int a[100010];
    int main()
    {
        for(int i=0; i<10; i++)
        {
            to['0'+i]=i;
        }
        for(int i=0; i<10; i++)
        {
            to['A'+i]=10+i;
        }
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            char s[3];
            scanf("%s",s);
            a[i]=0;
            for(int j=0; j<2; j++)
            {
                a[i]=16*a[i]+to[s[j]];
            }
        }
        long long ans=0;
        memset(dp,0xcf,sizeof(dp));
        dp[0][0]=0;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<min(i,257); j++)
            {
                dp[i][j]=max(dp[i-1][j]+(a[i]^(i-1-j)),dp[i-1][j-1]);
            }
            if(i<257)
            dp[i][i]=0;
        }
        for(int i=0; i<=256; i++)
        {
            ans=max(ans,dp[n][i]);
        }
        printf("%I64d
    ",ans);
        return 0;
    }
    View Code

    最后是合肥d题。。hdu5555

    题意:

    有一个n*n的矩阵,对每个1<=x<=n ( x, 0)处都有一只frog,它们会向上跳,同时每个y ( 1 <= y <= n)有一个跳板,跳板可能是好的(长度为n),也可能是坏的 (长度小于n)。已知frog们最多会跳10个坏的跳板, 现在要在跳板上的某些位置放n个药丸(每个跳板一个),问所有的frog都恰好吃一个药丸 并且能跳出矩阵的方案数

    思路:

    先考虑坏的跳板,由于每个x坐标最多有十个坏的,可以状压它们,代表这10个有没有被前面的frog用过(即在前面frog的 x 坐标处放过了药丸),我的代码里为1代表没被用

    注意在状态转移的过程中,某一个frog可能是在自己的坏跳板上吃了药丸,还可能在 好的跳板上吃了药丸,如果对每个frog 都向好的跳板转移,显然好的跳板是不够用的,那么怎么处理呢?

    思考后容易发现其实不需要处理,因为有且只有n个 药丸,如果前面某个跳板 还没有被使用就已经不能用了 显然这是一个非法的状态,那么直接不进行转移就好了 

    最后的答案就是dp[n][0]* (好的跳板个数)! ,为什么要乘阶乘呢,因为显然好的跳板位置是不同的,如果由不同的frog使用了 那么方案也是不同的

    感觉有点说不清楚了,具体见代码

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const long long mod=105225319;
    struct node
    {
        int l, r;
    };
    
    vector<node> v;
    vector<int> g[1010];
    long long dp[1010][1<<11];
    int l[1010];
    int r[1010];
    int getnum(int x,int t)
    {
        int res=-1;
        for(int i=0; i<(int)g[t].size(); i++)
        {
            if(g[t][i]==x)
                res=i;
        }
        return res;
    }
    int bz(int st,int now)
    {
        return ((st>>(now+1))<<(now+1)) + (st&((1<<now)-1));
    }
    long long f[1010];
    int main()
    {
        f[0]=1;
        for(int i=1; i<=1000; i++)
        {
            f[i]=f[i-1]*i%mod;
        }
        freopen("in.txt","r",stdin);
        int T,cas=1;
        scanf("%d",&T);
        while(T--)
        {
            v.clear();
            for(int i=1; i<=1000; i++)
            {
                g[i].clear();
            }
            int nn=0;
            int n,x,y;
            scanf("%d",&n);
            for(int i=0; i<n; i++)
            {
                scanf("%d",l+i);
            }
            for(int i=0; i<n; i++)
            {
                scanf("%d",r+i);
            }
            for(int i=0; i<n; i++)
            {
                x=l[i],y=r[i];
                if(y-x+1<n)
                {
                    v.push_back(node{x,y});
                }
                else
                {
                    nn++;
                }
            }
            for(int i=0; i<(int)v.size(); i++)
            {
                for(int j=v[i].l; j<=v[i].r; j++)
                {
                    g[j].push_back(i);
                }
            }
            int ok=1;
            for(int i=0; i<=n; i++)
            {
                if((int)g[i].size()>10)
                {
                    ok=0;
                }
            }
            if(!ok)
            {
                printf("Case #%d: 0
    ",cas++);
                continue;
            }
            memset(dp,0,sizeof(dp));
            dp[0][0]=1;
            for(int i=1; i<=n; i++)
            {
                for(int j=0; j<(1<<(int)g[i-1].size()); j++)
                {
                    int st=(1<<(int)g[i].size())-1;
                    if(dp[i-1][j]==0)
                        continue;
                    ok=1;
                    for(int k=0; k<(int)g[i-1].size(); k++)
                    {
                        if(j&(1<<k))
                        {
                            if(v[g[i-1][k]].r<i)
                            {
                                ok=0;
                            }
                        }
                        else
                        {
                            if(v[g[i-1][k]].r>=i)
                            {
                                int now=getnum(g[i-1][k],i);
                                st=bz(st,now);
                            }
                        }
                    }
                    if(ok)
                    {
                        for(int k=0; k<(int)g[i].size(); k++)
                        {
                            if(st&(1<<k))
                            {
                                dp[i][bz(st,k)]+=dp[i-1][j];
                                dp[i][bz(st,k)]%=mod;
                            }
                        }
                        dp[i][st]+=dp[i-1][j];
                        dp[i][st]%=mod;
                    }
                }
            }
            printf("Case #%d: %I64d
    ",cas++,f[nn]*dp[n][0]%mod);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    【二分图匹配/匈牙利算法】飞行员配对方案问题
    【模板/学习】匈牙利算法
    【tarjan缩点+分层图】草鉴定Grass Cownoisseur
    【微笑】
    【质因数分解】SAC E#1 一道中档题 Factorial
    【dfs+dp】砝码称重
    【背包dp】自然数拆分Lunatic版
    【单调队列】最大子序和
    【单调队列】滑动窗口
    bzoj 2834: 回家的路
  • 原文地址:https://www.cnblogs.com/oneshot/p/5038346.html
Copyright © 2011-2022 走看看