19-NYIST-ACM - 背包专题 [Cloned]

总题目🔗

A - 钱币兑换问题 HDU - 1284

思路

  1. 这一题让求用 n 种物品填满空间为 m 的背包(每个物品可以使用 无限次),这里需要注意 必须要 “填满” 背包才可以,
  2. 所以在初始化 dp 的时候,只有 dp [0] = 1, 其他的都初始化为 0,
  3. 至于状态转移方程:dp [j + i] += dp [j], 其实它是一个递推式,在状态为 j 的使用使用 空间为 i 的物品,可以让方案状态转移到 dp [j + i].

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int dp[mxn];

int main()
{
    /* fre(); */

    int n;
    while(~ sd(n))
    {
        memset(dp, 0, sizeof dp);
        dp[0] = 1;
        for_(i, 1, 3)
            for_(j, 0, n - i)
                dp[j + i] += dp[j];    

        pr("%d\n", dp[n]);
    }

    return 0;
}

B - Investment POJ - 2063

思路

  1. 首先明确,在某个一年我们手中的总钱数为 m 的时候,要通过合理的选择来买股票,来获取最大利润,这是一个 完全背包问题,股票的价格是空间,股票的利润是:物品的价值;
  2. 因此我们可以将每一年都看作是一个完全背包问题,只不过在每年进行的 dp 时候,手里的钱会因为上一年的利润而增加(背包的空间增加),

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int v[mxn], w[mxn], s;

ll dp[mxn];

int main()
{
    /* fre(); */

    int T; sd(T);
    while(T --)
    {
        ll n, m;
        sc("%lld %lld", &n, &m);
        sd(s);
        for_(i, 1, s) 
            sc("%d %d", &v[i], &w[i]);

        for_(i, 1, m)           //枚举年数
        {
            memset(dp, 0, sizeof(dp));
            for_(j, 1, s)       //枚举物品
            {
                for(ll k = 0; k * 1000 <= n; k += 1)
                    if(k - v[j] / 1000 >= 0)
                        dp[k] = max(dp[k], dp[k - v[j] / 1000] + w[j]);
            }
            n += dp[n / 1000];
        }

        pr("%lld\n", n);
    }

    return 0;
}

C - Charlie’s Change POJ - 1787 (记录最大状态转移)

思路

  1. 这一题很有做的意义,
  2. 首先明确这题是一个多重背包问题(因为每种硬币的使用次数是有限的),不过在代码我们,但是正常的 for 循环是正向(正常的背包问题是 逆向 for 循环的),这一点很奇怪但是也很巧妙,
  3. 在这道题中引入了一个 use [] 数组来统计当前,当前 for 循环在讨论的 “改种硬币” 已经使用的数量,通过 use 来时限制 当前种类的硬币不要使用超量了,
  4. 这一题,让求的是把背包填满所需的最大硬币个数,我们就要考虑 dp [] 中维护的值的意义是什么,这一题中 dp [j] 的值维护的是:当前背包空间为 j 的时候,装满背包的硬币的最大数量,也就是 1 分的硬币的 的价值和 3 分的硬币的权值都是 1,由于硬币的权值关系我们应该多使用 1 分的硬币,之后多使用 2 分的…
  5. 这题还用了一个 path [] 数组,path [j] 表示在位置为 j 的时候 是从那个转态转移过来的,我们回溯的使用通过两个状态的转移 “距离差值”,就是使用的硬币的种类,这样我们回溯的时候,就可以统计出使用过哪些硬币了,,

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
int dp[mxn];
int val[mxn], num[mxn];
int use[mxn];               //use[i] 表示的是背包空间为i的时候,使用的当前的种类的硬币的数量
int path[mxn];
map<int, int> ans;
int n;

int main()
{
    /* fre(); */

    val[1] = 1; val[2] = 5; val[3] = 10; val[4] = 25;
    while(sd(n) && n)
    {
        ans.clear();
        for_(i, 1, 4) sd(num[i]);

        for_(i, 1, n) dp[i] = - INF;
        for_(i, 1, 4)
        {
            memset(use, 0, sizeof use);
            for_(j, val[i], n)
            {
                //注意:在这一题中我们 dp值维护的是什么?,dp[i] 表示在背包空间为i的情况下 填满背包的最大硬币数量
                //注意:这里 第二层for循环是正向的,正向for循环在背包问题中是用来解决 “完全背包的”, 但是在这一题中 每种硬币的数量是有限的,是一个 “多重背包问题”, 但是我们同 use[] 数组的使用 来统计已经使用的当前种类的硬币的数量,当数量超过所给的该种的硬币的数量的时候,那么此时的状态转移是非法的,那么禁止这个时候的状态转移就行了

                if(use[j - val[i]] < num[i] && dp[j - val[i]] >= 0 && dp[j - val[i]] + 1 > dp[j])
                {
                    dp[j] = dp[j - val[i]] + 1;         
                    use[j] = use[j - val[i]] + 1;       //当前正在使用过的 第i种硬币数量 +1
                    path[j] = j - val[i];               //很巧妙的记录路径, 在当前背包空间为j是,记录的当前方案的路径是从 背包空间j - val的状态注意过来,这样我们通过 j - (j - val) 就可以得出当前这一步的状态转移,是使用的第i种硬币
                }
            }
        }

        if(dp[n] < 0)
        {
            pr("Charlie cannot buy coffee.\n");
            continue;
        }

        int p = n;
        while(p)
        {
            ans[p - path[p]] ++;            
            p = path[p];
        }

        pr("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", ans[1], ans[5], ans[10], ans[25]);
    }

    return 0;
}


D - 饭卡 HDU - 2546

思路

  1. 这一题不是一个背包问题,感觉就像一道递推题,,,
  2. 我们先个 总钱数 m + 50, 这样我们在递推的时候不会出现下表为负的下标,
  3. 我们在刚开始标记 dp [m + 50] = 1, 其他的为零,之后

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 2005;
int dp[mxn];
int v[mxn];


int main()
{
    /* fre(); */
    int n;
    while(sd(n) && n)
    {
        for_(i, 1, n) sd(v[i]);
        int m; sd(m);

        sort(v + 1, v + 1 + n);

        memset(dp, 0, sizeof dp);
        dp[m + 50] = 1;

        int ans = m + 50;           //注意ans必须初始化为 m + 50, 不能初始化为其他的如 INF 等,因为当 n == 0的时候,此时卡里面有多少钱,答案就是多少钱
        for_(i, 1, n)
        {
            for_(j, 55, m + 50)
            {
                if(dp[j])
                {
                    dp[j - v[i]] = 1;
                    ans = min(ans, j - v[i]);
                }
            }
        }

        pr("%d\n", ans - 50);
    }

    return 0;
}

D - 饭卡 HDU - 2546 (贪心 +dp)

思路

  1. 这一题的动态规划不向之前的背包问题,这一球的是把卡里面的余额 m,通过买 n 中食物之后,能够把卡里面的余额最小变成多少,可以变成负数(当卡里面的余 >=5 时,即使卖了某个物品之后变成负数,这种情况也是允许的)
  2. 这一题的 dp 做法就是,首先我们将 m += 50, 这样避免在枚举的时候出现下表为负数的情况,
  3. 我们首先标记 dp [m+50] = 1, 表面这个只有这个状态是合法的转移状态,刚开始当我们在买一件价格为 x 物品的的时候,那么就存在:设 j = m + 50, 如果 dp [j] == 1, 那么 dp [j - x] = 1 (这个时候产生了一个 合法转移状态 dp [j - x]); 如果 dp [j] == 0, 说明这个时候不能从 j 这个状态进行转移,
  4. 这样我们 不断的枚举每个物品,进行从已有的合法状态,进行状态转移产生出新的合法状态,
  5. 最后的 我们的答案就是 最小的那个合法状态的下标减去 50 之后就是答案,
  6. 我们在枚举的物品进行状态转移的时候应该,从价格小的 到 价格大的 顺序开始枚举,因为这样才可能产生出 更大的负数

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 2005;
int dp[mxn];
int v[mxn];


int main()
{
    /* fre(); */
    int n;
    while(sd(n) && n)
    {
        for_(i, 1, n) sd(v[i]);
        int m; sd(m);

        sort(v + 1, v + 1 + n);

        memset(dp, 0, sizeof dp);
        dp[m + 50] = 1;

        int ans = m + 50;           //注意ans必须初始化为 m + 50, 不能初始化为其他的如 INF 等,因为当 n == 0的时候,此时卡里面有多少钱,答案就是多少钱
        for_(i, 1, n)
        {
            for_(j, 55, m + 50)
            {
                if(dp[j])
                {
                    dp[j - v[i]] = 1;
                    ans = min(ans, j - v[i]);
                }
            }
        }

        pr("%d\n", ans - 50);
    }

    return 0;
}

E - Bone Collector II HDU - 2639

思路

  1. 经点的第 k 大问题,我们在 dp 的时候,我们对每个状态都维护前 k 大,之后下一个状态的前 k 大一定可以从当前状态的前 k 大转移过来,

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1010;
int f[mxn][mxn];
int n, m, k;
int v[mxn], w[mxn];
int a[mxn], b[mxn];

int main()
{
    /* fre(); */
    int T; sd(T);   
    while(T --)
    {
        sc("%d %d %d", &n, &m, &k);

        for_(i, 1, n) sd(w[i]);
        for_(i, 1, n) sd(v[i]);

        memset(f, 0, sizeof f);

        for_(i, 1, n)
        {
            rep_(j, m, v[i])
            {
                for_(l, 1, k)           //处理两种情况的状态转移方程分别存储到a、b数组中
                {
                    a[l] = f[j][l];
                    b[l] = f[j - v[i]][l] + w[i];
                }

                a[k + 1] = b[k + 1] = -1;       //结束标志
                int x = 1, y = 1, z = 1;
                while((x <= k || y <= k) && z <= k)         //注意让求的第k大是不是严格第k大
                {
                    if(a[x] > b[y])
                    {
                        f[j][z] = a[x ++];
                    }
                    else
                    {
                        f[j][z] = b[y ++];
                    }
                    if(f[j][z] != f[j][z - 1]) z ++;        //非严格第k大 只有不想等 z 才++
                }
            }
        }

        pr("%d\n", f[m][k]);

    }




    return 0;
}

F - Coin Change UVA - 674

思路

  1. 参考 题解第 1 题

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e4;
int dp[mxn];
int cnt[mxn];
int v[] = { 0, 1, 5, 10, 25, 50 };

int main()
{
    /* fre(); */
    int n;
    while(sd(n) != EOF)
    {
        for_(i, 1, n) dp[i] = - INF;
        memset(cnt, 0, sizeof cnt);
        cnt[0] = 1; 

        for_(i, 1, 5)
        {
            for_(j, v[i], n)
            {
                int val = dp[j - v[i]] + v[i];
                if(val > dp[j])
                {
                    dp[j] =val;
                    cnt[j] = cnt[j - v[i]];
                }
                else if(val == dp[j])
                    cnt[j] += cnt[j - v[i]];
            }
        }
        pr("%d\n", cnt[n]);
    }




    return 0;
}


G - Dollars UVA - 147

思路

  1. 这一题我们注意浮点数扩大倍数产生精度问题,解决方案:假如有个浮点数 f,扩大 k 倍之后变成整数 n: n = k ∗ f + 0.5 n = k*f+0.5 n=kf+0.5
    精度问题分析

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e5;
ll dp[mxn];
ll cnt[mxn];
int v[] = { 0, 1, 2, 4, 10, 20, 40, 100, 200, 400, 1000, 2000 };

int main()
{
    /* fre(); */
    db t;
    while(sc("%lf", &t) && t)
    {
        int n = t * 20;
        for_(i, 1, n) dp[i] = -INF;
        memset(cnt, 0, sizeof cnt);
        cnt[0] = 1;

        for_(i, 1, 11)
        {
            for_(j, v[i], n)
            {
                int val = dp[j - v[i]] + v[i];
                if(val > dp[j])
                {
                    dp[j] = val;
                    cnt[j] = cnt[j - v[i]];
                }
                else if(val == dp[j])
                    cnt[j] += cnt[j - v[i]];
            }
        }

        pr("%6.2f%17lld\n", t, cnt[n]);
    }



    return 0;
}





H - 最大报销额 HDU - 1864

思路

  1. 01 背包问题,只不过我们需要对数据进行预处理,
  2. 有一点题意需要理解:每饭票上的 “某种类型的 x 金额不超过 600”,一张发票上可能会重复出现 x,而这些 x 类型的金额之和也不应该超过 600,,,,
  3. 这一有两种 dp 思路,一种就是将总金额 m = m* 100/5 ,之后将 m 作为背包的空间,进行 01 背包那样的 dp 就行了
  4. 但是我在搜索题解的时候发现了另外一种神器的 dp 思路:先看一下代码表示
        for(i=1;i<=n;i++)
        {
            for(j=n;j>=1;j--)
            {
                if(flag[i]==0)
                    dp[j]=max(dp[j],dp[j-1]+sum[i]);
            }
        }
  1. 我们可以看到 上面这段代码的第二层 for 循环 循环的是物品数量 n(而正常的 01 背包循环的是 背包的空间 m),那么它这样 for 循环合理吗?有意义吗?,
  2. 经过了一番模拟发现是合理的,在这段代码里:dp [j] 表示在当前有 i 个物品的时候,从 i 个物品中选择 j (j<=i) 个物品,可以产生的最大价值为:dp [j],
  3. 真是一种神器的 dp 方式。。。。

代码一

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
 
using namespace std;
 
#define mem(a, i) memset(a, i, sizeof(a))
#define INF 0x3f3f3f3f
 
int dp[4999000];
 
int main()
{
    //freopen("in.txt", "r", stdin);
    int n;
    double q;
    while(cin >> q >> n && n)
    {
        int w[1010];
        int m;
        char s, c;
        mem(w, 0);
        for(int i = 1; i <= n; ++ i)
        {
            cin >> m;
            bool flag = true;
            double h, A = 0, B = 0, C = 0, sum = 0;
            for(int j = 0; j < m; ++ j)
            {
                c = getchar();
                s = getchar();
                c = getchar();
                if(!(s == 'A' || s == 'B' || s == 'C'))
                {
                    flag = false;
                }
                cin >> h;
                if(s == 'A')
                {
                    A += h;
                }
                else if(s == 'B')
                {
                    B += h;
                }
                else if(s == 'C')
                {
                    C += h;
                }
            }
            if(A > 600 || B > 600 || C > 600)
            {
                flag = false;
            }
            else
            {
                sum += (A + B + C);
            }
            if(flag && sum <= 1000)
            {
                w[i] = (sum * 100);
            }
            else
            {
                w[i] = INF;
            }
        }
        mem(dp, 0);
        int p = (q * 100);
        for(int i = 1; i <= n; ++ i)
        {
            for(int j = q * 100; j >= 0; --j)
            {
                if(j < w[i])
                {
                    dp[j] = dp[j];
                }
                else
                {
                     dp[j] = max(dp[j - w[i]] + w[i], dp[j]);
                }
            }
        }
        printf("%.2lf\n", dp[p] * 1.0 / 100);
    }
    return 0;
}


代码二

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#define max(a,b) a>b?a:b
using namespace std;

int main()
{
    int n,m,i,j,flag[35];
    double p[35];
    char ch;
    double sum[35],dp[35];
    double max;
    while(scanf("%lf%d",&max,&n),n)
    {
        for(i=1;i<=n;i++)
            sum[i]=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&m);
            flag[i]=0;
            double suma=0,sumb=0,sumc=0;
            for(j=1;j<=m;j++)
            {
                getchar();
                scanf("%c:%lf",&ch,&p[j]);
                sum[i]+=p[j];
                if(ch=='A')
                    suma+=p[j];
                else if(ch=='B')
                    sumb+=p[j];
                else if(ch=='C')
                    sumc+=p[j];
                else
                    flag[i]=1;
            }
            if(sum[i]>1000 || suma>600 || sumb>600 || sumc>600)
                flag[i]=1;
        }
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            for(j=n;j>=1;j--)
            {
                if(flag[i]==0)
                    dp[j]=max(dp[j],dp[j-1]+sum[i]);
            }
        }
        double maxn=-1;
        for(i=1;i<=n;i++)
        {
            if(dp[i]>maxn && dp[i]<=max)
                maxn=dp[i];
        }
        printf("%.2lf\n",maxn);
    }
    return 0;
}

I - Bone Collector HDU - 2602

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int mxn = 1e5;

int dp[mxn];
int v[mxn], w[mxn];


int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, m;
        memset(dp, 0, sizeof dp);
        scanf("%d %d", &n, &m);
        for(int i = 0; i < n; i ++)
            scanf("%d", &w[i]);
        for(int i = 0; i < n; i ++)
            scanf("%d", &v[i]);
        for(int i = 0; i < n; i ++)
            for(int j = m; j >= v[i]; j --)
            dp[j] = max(dp[j], dp[j - v[i]] +w[i]);

        printf("%d\n", dp[m]);

    }



    return 0;

}

J - Proud Merchants HDU - 3466

思路

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <set>
#include <bitset>
#include <vector>
#include <stack>
#include <map>
/* #include <unordered_map> */
#include <sstream>
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<int, int>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)
#define sc scanf
#define pr printf
#define sd(a) scanf("%d", &a)
#define ss(a) scanf("%s", a)
using namespace std;

const int mxn = 1e4;
int dp[mxn];

struct Node
{
    int p, q, w;
} da[mxn];
bool cmp(Node a, Node b)
{
    return a.q - a.p < b.q - b.p;
}

int main()
{
    /* fre(); */
    int n, m;
    while(~ sc("%d %d", &n, &m))
    {
        memset(dp, 0, sizeof dp);
        for_(i, 1, n)
        {
            sc("%d %d %d", &da[i].p, &da[i].q, &da[i].w);
        }
        sort(da + 1, da + 1 + n, cmp);

        for_(i, 1, n)
        {
            rep_(j, m, da[i].q)
                dp[j] = max(dp[j], dp[j - da[i].p] + da[i].w);
        }

        pr("%d\n", dp[m]);
    }


    return 0;
}


K - 悼念 512 汶川大地震遇难同胞 —— 珍惜现在,感恩生活 HDU - 2191

思路

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int mxn = 1e5;

int dp[mxn];
int n, m;
int v, w, s;

void sov(int t)
{
    for(int j = m; j >= t * v; j --)
    {
        dp[j] = max(dp[j], dp[j - t * v] + t * w);
    }

}


int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {

        memset(dp, 0, sizeof dp);
        scanf("%d %d", &m, &n);

        for(int i = 0; i <n; i ++)
        {
            scanf("%d %d %d", &v, &w, &s);
            int t = 1;
            while(s >= t)
            {
                sov(t);
                s -= t;
                t <<= 1;
            }

            if(s) sov(s);
        }


        printf("%d\n", dp[m]);

    }



    return 0;

}

L - 湫湫系列故事 —— 减肥记 I HDU - 4508

思路

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int mxn = 1e5;

int dp[mxn];
int n, m;
int v[mxn], w[mxn];



int main()
{
    while(~ scanf("%d", &n))
    {
        
        memset(dp, 0, sizeof dp);
        for(int i = 0; i <n; i ++)
            scanf("%d %d", &w[i], &v[i]);
        scanf("%d", &m);
        
        for(int i = 0; i < n; i ++)
            for(int j = v[i]; j <= m; j ++)
            dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
        
        
        printf("%d\n", dp[m]);

    }



    return 0;

}

思路

代码



M - Dividing HDU - 1059

思路

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int mxn = 1e5;

int dp[mxn];
int n, m;
int v[mxn], w[mxn];
int s[mxn];

void sov(int t, int w)
{
    for(int j = m; j >= t * w; j --)
        dp[j] = max(dp[j], dp[j - t * w] + t * w);
    
}

int main()
{
    int cas = 1;
    while(scanf("%d %d %d %d %d %d", &s[1], &s[2], &s[3], &s[4], &s[5], &s[6]))
    {
        if(s[1] + s[2] + s[3] + s[4] + s[5] + s[6] == 0) break;    
        
        memset(dp, 0, sizeof dp);
        printf("Collection #%d:\n", cas ++);    
        m = 0;
        for(int i = 1; i <= 6; i ++)
        {
            m += (s[i] * i);
        };
        
        if(m % 2)
        {
            printf("Can't be divided.\n");
            printf("\n");
            continue;
        }
        m /= 2;
        for(int i = 1; i <= 6; i ++)
        {
            int t = 1;
            while(s[i] >= t)
            {
                s[i] -= t;
                sov(t, i);
                t <<= 1;
            }
            
            if(s[i]) sov(s[i], i);
        }
        
        if(dp[m] == m)
            printf("Can be divided.\n");
        else
            printf("Can't be divided.\n");

        printf("\n");
    }



    return 0;

}


N - Piggy-Bank HDU - 1114

  1. 首先不难发现这一题是一个完全背包,但是这一题让求的是,通过合理的选择物品,使背包恰装满的最小价值是多少,我们将 转态转移方程中的 max 变成 min 就行了,
  2. 因为恰好装满背包 且 是求 最小价值, 所以我们在初始化的时候,要除了 dp [0]=0, 其他的都要初始化为:INF

思路

代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;


const int mxn = 1e5;
struct Node
{
    int v, w;
    double x;
} p[mxn];

bool cmp(Node a, Node b)
{
    return a.x > b.x;
}

int dp[mxn];
int n, m;

int main()
{
    int T;
    scanf("%d", &T);
    while(T --)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        m = b - a;
        scanf("%d", &n);
        
        memset(dp, 0x3f3f3f3f, sizeof dp);
        dp[0] = 0;
        
        for(int i = 0; i < n; i ++)
        {
            scanf("%d %d", &p[i].w, &p[i].v);
            p[i].x = 1.0 * p[i].v / p[i].w;
        }

        for(int i = 0; i < n; i ++)
            for(int j = p[i].v; j <= m; j ++)
            {
                dp[j] = min(dp[j], dp[j - p[i].v] + p[i].w);
            }
        
        if(dp[m] < 1e6)
        {
            printf("The minimum amount of money in the piggy-bank is %d.\n", dp[m]);
        }
        else
            printf("This is impossible.\n");
    }
    
    
    
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值