zoukankan      html  css  js  c++  java
  • 背包问题

    https://vjudge.net/contest/256610#problem/Q

    dp完全背包

    现在一共有N个程序员,一个程序员(编号为i)写一行代码会有Ai个bug.目的是完成一个M行的程序。

    第i个程序员会写Vi行代码,对应就会出现Vi*Ai个Bug.

    问将这个M行的程序写完之后,Bug数量不超过b个的分配方案数。

    设定dp【i】【j】表示一共写了i行代码,一共写出来了j个Bug的方案数。

    那么很简单的递推方程:dp【i】【j】=dp【i-1】【j-Ai】;

    那么ans=Σdp【m】【i】(0<=i<=b)

    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int dp[505][500*4];
    int main()
    {
        int n,m,b,mod;
        while(~scanf("%d%d%d%d",&n,&m,&b,&mod))
        {
            memset(dp,0,sizeof(dp));
            dp[0][0]=1;
            for(int z=0;z<n;z++)
            {
                int x;
                scanf("%d",&x);
                for(int i=1;i<=m;i++)
                {
                    for(int j=x;j<=b;j++)
                    {
                      dp[i][j]+=dp[i-1][j-x];
                       dp[i][j]%=mod;
                    }
                }
            }
            int ans=0;
            for(int i=0;i<=b;i++)
            {
                ans+=dp[m][i];
                ans%=mod;
            }
            printf("%d
    ",ans);
        }
    }

    https://vjudge.net/contest/258242#problem/I

    记忆化

     可以一步一步跳,也可以跳到后面一个颜色相同的点。。

    dp,然后要记录某个颜色最近的那个点的坐标,然后状态转移。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxm = 2e5 + 5;
    int a[maxm], b[maxm], c[maxm];
    int n, num;
    int main()
    {
       scanf("%d", &num);
       while(num)
       {
           memset(b, -1, sizeof(b));
           scanf("%d", &n);
           for(int i = 1; i <= n; i++){
                scanf("%d", &a[i]);
           }
            c[1] = 0;
            b[a[1]] = 0;
            for(int i = 2; i <= n; i++)
               {
                   if(b[a[i]] != -1)
                   {
                       c[i] = min(c[i-1] + 1, b[a[i]] + 1);
                       b[a[i]] = c[i];
                   }
                   else
                   {
                        c[i] = c[i-1] + 1;
                       b[a[i]] = c[i];
                   }
            }
               printf("%d
    ", c[n]);
               num--;
       }
       return 0;
    }

    https://vjudge.net/contest/258509#problem/I

    抽屉原理,可以用dp

    n个数字,是否存在一个序列是的模上m等于0,

    根据抽屉原理,n > m是一定满足(看前缀和)

    然后就是dp了(状态转移的那种dp(记录两个dp数组来转移))

    #include<cstdio>
    #include<cstring> 
    const int N = (int)1e6 + 5;
    int n, m;
    int a[N];
    bool dp[2][1000];//滚动数组 
    bool work(){
        if(n > m) return true;//多加这一句,TLE的代码说不定就能AC 
        dp[0][a[0]] = true;
        for(int i = 1; i < n; i ++){
            memset(dp[i & 1], 0, sizeof(bool)*1000);
            dp[i & 1][a[i]] = true;
            for(int j = 0; j < m; j ++){
                if(dp[(i-1) & 1][j]){
                    dp[i & 1][(j + a[i]) % m] = true;
                    dp[i & 1][j] = true;
                }
            }
            if(dp[i & 1][0]) return true;
        }
        return dp[(n-1) & 1][0];
    }
    int main(){
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i ++){
            scanf("%d", &a[i]);
            a[i] %= m;
        }
        puts(work() ? "YES" : "NO");
    }

    https://vjudge.net/contest/264760#problem/F

    字符串dp

    给定一个括号字符串,然后两头加上其他字符串,保证每个前缀的左括号数量大于等于右括号数量,并且总体点的左括号等于右括号数量。

    把左括号看成+1,右括号看成-1.先统计字符串s中‘(’的最小前缀和为minn,s中前缀和为x,那么p只需要满足p的前缀和+minn>=0,符合这样的不等式的p都是可行解,那么我们预处理出一个dp[i][j],代表i个括号平衡度为j的个数,那么之后枚举p的长度i和p的平衡度j,在满足 p的前缀和(j)+minn>=0 和 q的前缀和(j+x)<=m-n-i(及q的长度) 其实在这里q的前缀和应该为-(j+x),因为总体前缀和应为0 但因为对称 所以可看成j+x

    #include<cstdio>

    #include<algorithm>
    using namespace std;
    const int maxn = 1e5 + 200;
    const int mod = 1e9 + 7;
    char s[maxn];
    long long dp[2500][2500];
    int main()
    {
        int n, m;
        scanf("%d%d", &n, &m);
        scanf("%s", s + 1);
        dp[0][0] = 1;
    for (int i = 1; i <= n - m; i++)//预处理长度为n-m,平衡度为0~i的dp[i][j] for (int j = 0; j <= i; j++) if (j > 0)dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j + 1])%mod; else dp[i][j] = dp[i - 1][j+1]; int minn = m+1,x=0; for (int i = 1; i <= m; i++) { if (s[i] == '(')x++; else x--; minn = min(minn, x); } long long ans = 0; for (int i = 0; i <= n - m; i++) for (int j = 0; j <= i; j++) if (j + minn >= 0 && (j + x) <= n - m - i) ans = (ans + dp[i][j] * dp[n - m - i][(j + x)]) % mod; printf("%I64d ", ans); }

    多重背包

    Cash Machine

     POJ - 1276

    多重背包题 , 二进制优化

    给出一个金币值,然后给出每种金币的值和数量,然后求出小于该金币值的最大的值。

    //这一种是加一个num数组,相当于代替了一套循环,来限制那种物品能拿多少个
    #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxm = 1e5 + 5; int n, m; int c[maxm], v[maxm]; int dp[maxm], num[maxm]; int main() { while(~scanf("%d%d", &n, &m)) { memset(dp, 0, sizeof(dp)); for(int i = 1; i <= m; i++) scanf("%d%d", &c[i], &v[i]); if(n == 0 || m == 0) { printf("0 "); continue; } for(int i = 1; i <= m; i++) { memset(num, 0, sizeof(num)); for(int j = v[i]; j <= n; j++) { if(dp[j - v[i] ] + v[i] > dp[j] && num[j - v[i] ] < c[i]) { dp[j] = dp[j - v[i] ] + v[i]; num[j] = num[j - v[i] ] + 1; } } } printf("%d ", dp[n]); } return 0; }
    //这种做法是把有多个的物品按二进制分开,1个算一种,2个算一种,4个算一种等,然后按照01背包的解法来解
    #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxm = 1e5 + 5; int n, m; int c[maxm], v[maxm], w[maxm * 100]; int dp[maxm], num[maxm]; int cnt; void check(int u, int v) { int flag = 1; while(1) { if(u >= flag) { u -= flag; w[++cnt] = flag * v; flag *= 2; } else { w[++cnt] = u * v; break; } } } int main() { while(~scanf("%d%d", &n, &m)) { memset(dp, 0, sizeof(dp)); cnt = 0; for(int i = 1; i <= m; i++) scanf("%d%d", &c[i], &v[i]), check(c[i], v[i]); if(n == 0 || m == 0) { printf("0 "); continue; } for(int i = 1; i <= cnt; i++) { for(int j = n; j >= w[i]; j--) { dp[j] = max(dp[j], dp[j - w[i] ] + w[i]); } } printf("%d ", dp[n]); } return 0; }

     https://blog.csdn.net/chuck001002004/article/details/50340819

    https://blog.csdn.net/qq_40679299/article/details/81978770

     Ant Counting

     POJ - 3046 

    N中蚂蚁,每种蚂蚁有几个,然后每次排除1个2个或者全部,求s 到 r的方案数之和,注意是集合

    这一种方法是按集合的思想来的,对每一种物品内来讨论,对于第一种蚂蚁,派0个,一个,两个,三个的方法都是1,对于第二种蚂蚁他派出蚂蚁就可以继承前面的某一个范围的和(更自己有多少个有关)。
    #include<cstdio> #include<cstring> using namespace std; const int N = 1e5 + 5; const int MOD = 1000000; typedef long long ll; int dp[N]; int sum[N];//前缀和数组 int cnt[1005]; int main() { int T, A, S, B, id; while(~scanf("%d%d%d%d", &T, &A, &S, &B)) { int low; memset(dp, 0, sizeof(dp)); memset(cnt, 0, sizeof(cnt)); for(int i = 0; i < A; i++) { scanf("%d", &id); cnt[id]++; } for(int i = 0; i <= cnt[1]; i++) dp[i] = 1; sum[0] = 1; int cur = cnt[1]; for(int i = 2; i <= T; i++) { cur += cnt[i]; for(int j = 1; j <= cur; j++) { sum[j] = (sum[j - 1] + dp[j]) % MOD; } for(int j = cur; j >= 1; j--) { low = j - cnt[i] - 1; if(low >= 0) dp[j] += sum[j - 1] - sum[low]; else dp[j] += sum[j - 1]; dp[j] = (dp[j] % MOD + MOD) % MOD; } } ll ans = 0; for(int i = S; i <= B; i++) { ans += dp[i]; } printf("%lld ", ans % MOD); } return 0; }
    //这一种是按多重背包的思想来写的
    #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxm = 1e3 + 5; const int mod = 1e6; int a[maxm]; int dp[2][maxm * 100]; int cur, pre; int main() { int T, A, S, B; while(~scanf("%d%d%d%d", &T, &A, &S, &B)) { int x; memset(dp, 0, sizeof(dp)); memset(a, 0, sizeof(a)); while(A--) { scanf("%d", &x); a[x]++; // printf("%d ", a[x]); } dp[0][0] = 1; for(int i = 1; i <= T; i++) { cur = i & 1; pre = (i - 1) & 1; memset(dp[cur], 0, sizeof(dp[cur])); for(int k = 0; k <= a[i]; k++) { for(int j = 0; j <= B; j++) { if(j >= k) dp[cur][j] = (dp[cur][j] + dp[pre][j - k]) % mod; } } } int res = 0; // cur = T & 1; for(int i = S; i <= B; i++) { res = (res + dp[cur][i]) % mod; // printf("%d ", res); } printf("%d ", res); } return 0; }
  • 相关阅读:
    static和final
    java面向对象白话解说
    方法
    数组
    JDK的安装和java程序的开发步骤以及环境变量配置
    VS2010 根据模型生成数据库 打开edmx.sql文件时 vs出现无响应的解决方案
    js简易写法
    .NET程序性能优化基本要领
    数据采集类
    ASP.NET MVC 3 配置EF自动生成模型
  • 原文地址:https://www.cnblogs.com/downrainsun/p/9975390.html
Copyright © 2011-2022 走看看