背包问题

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\n",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\n", 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\n", 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\n"); 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\n", 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\n"); 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\n", 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\n", 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\n", 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\n", res); } printf("%d\n", res); } return 0; }

 

posted @ 2018-11-17 20:07  downrainsun  阅读(250)  评论(0编辑  收藏  举报