zoukankan      html  css  js  c++  java
  • 01背包入门题集

    整理一下我之前做过的01背包的基础题目,以后不断更新

    PS:限于篇幅,每道题目只给出粗略分析以及核心代码

     

    1. POJ 3624 Charm Bracelet

      这题就是赤裸裸的01背包裸题,不过开二维的数组似乎不行,那么一维能节省空间。背包的第二个for循环是倒序的,这是因为dp[i][j]是由dp[i-1][j-v[i]]转移来的。如果顺序的话,是从dp[i][j-v[i]]转移来的,那是完全背包的状态转移方程。

      代码:

    memset (dp, 0, sizeof (dp));
        for (int i=1; i<=n; ++i)    {
            for (int j=m; j>=v[i]; --j) {
                if (dp[j] < dp[j-v[i]] + w[i])  {
                    dp[j] = dp[j-v[i]] + w[i];
                }
            }
        }
    printf ("%d
    ", dp[m]);
    

    2. POJ 3628 Bookshelf 2

      这题比上一题绕了个弯,求超过b的最小高度。先定背包容量为总高度sum,dp[j] 表示当前容量为j时的最大高度为多少。然后从b开始枚举,当dp[j] >= b时,那么dp[j] - b一定是最小高度差

      代码:

    int sum = 0;
    for (int i=1; i<=n; ++i)    {
        scanf ("%d", &h[i]);
        sum += h[i];
    }
    for (int i=0; i<=sum; ++i)  dp[i] = 0;
    for (int i=1; i<=n; ++i)    {
        for (int j=sum; j>=h[i]; --j)   {
            if (dp[j] < dp[j-h[i]] + h[i])  {
                dp[j] = dp[j-h[i]] + h[i];
            }
        }
    }
    int ans = INF;
    for (int i=b; i<=sum; ++i)  {
        if (dp[i] >= b)  {
            ans = dp[i] - b;    break;
        }
    }
    printf ("%d
    ", ans);
    

      

    3. HDOJ 2546 饭卡

      首先将m - 5,不考虑最贵的菜(剩下的钱+5可以买下最贵的菜),那么前n-1道菜用01背包,dp[j] 表示用j余额最大的消费,最后处理最贵的菜。

      代码:

    if (m < 5)  {
    	printf ("%d
    ", m);
    }
    else    {
    	sort (price+1, price+n+1);
    	int mx = price[n];
    	m -= 5;
    	memset (dp, 0, sizeof(dp));
    	for (int i=1; i<=n-1; ++i)  {
    		for (int j=m; j>=price[i]; --j) {
    			dp[j] = max (dp[j], dp[j-price[i]] + price[i]);
    		}
    	}
    	printf ("%d
    ", m + 5 - dp[m] - mx);
    }
    

      

    4. POJ 1948 Triangular Pastures

      首先我们考虑给出3边求面积,用到海伦公式P = (a + b + c) / 2.0; S = sqrt(p * (p - a) * (p - b) * (p - c))。因为每条木板最多只能用到一次,用01背包写~ 二维dp:dp[i][j] = true 表示边i和边j能构成一个三角形,那么第三边就是sum - i - j。其次,在一个三角形中,每条边最大是sum / 2;

      代码:

    int sum = 0;
    for (int i=1; i<=n; ++i)    {
        scanf ("%d", &l[i]);    sum += l[i];
    }
    int half = sum / 2;
    memset (dp, false, sizeof (dp));
    dp[0][0] = true;
    for (int i=1; i<=n; ++i)    {
        for (int j=half; j>=0; --j) {
            for (int k=j; k>=0; --k)    {
                if ((j >= l[i] && dp[j-l[i]][k]) || k >= l[i] && dp[j][k-l[i]]) {
                    dp[j][k] = true;
                }
            }
        }
    }
    int ans = -1;
    for (int i=half; i>=1; --i) {
        for (int j=i; j>=1; --j)    {
            int k = sum - i - j;
            if (dp[i][j] && ok (i, j, k))   {
                ans = max (ans, area (i, j, k));
            }
        }
    }
    printf ("%d
    ", ans);
    

      


    5. POJ 1837 Balance
      设f[i][j]代表挂了前i个砝码时,平衡度为j的方法的个数。显然,平衡度的范围为[-25*20*15,25*20*15],即[-7500,7500],但是要使天平保持平衡,一旦平衡度超过3750,或小于-3750,那么剩下的砝码无论如何放,天平都无法平衡,所以这种情况可以不用考虑。当然,实际时,由于不允许有负数,所以f[i][j]需要进行必要的修改。

      代码:

    memset (dp, 0, sizeof (dp));
    dp[0][3750] = 1;                                    //修改成3750为平衡点 
    for (int i=1; i<=m; ++i)    {
        for (int j=0; j<=15000; ++j)    {
            if (dp[i-1][j]) {                           //从3750开始
                for (int k=1; k<=n; ++k)    {           //可能下垂任何钩的重量,但他是被迫使用所有的重量
                    dp[i][j+l[k]*w[i]] += dp[i-1][j];   //dp[i][j] 代表挂了前i个砝码时,平衡度为j的方法的个数
                }
            }
        }
    }
    printf ("%d
    ", dp[m][3750]);                       //输出平衡点的个数
    

    6. POJ 2184 Cow Exhibition

      给出num(num<=100)头奶牛的S和F值(-1000<=S,F<=1000),要求在这几头奶牛中选出若干头,使得在其总S值TS和总F值TF均不为负的前提下,求最大的TS+TF值,可以把S当体积,F当价值做01背包。但是注意是S可为负,所以整体加100000,然后要注意DP顺序,S为负是要顺序,为正时逆序。如果上一题会的话,这一题也不难。

      代码:

    for (int i=0; i<=200000; ++i)   dp[i] = -INF;
    dp[100000] = 0;
    for (int i=1; i<=n; ++i)
    {
        if (v[i] > 0)   {
            for (int j=200000; j>=v[i]; --j)    {
                if (dp[j-v[i]] > -INF)
                    dp[j] = max (dp[j], dp[j-v[i]] + w[i]);
            }
        }
        else    {
            for (int j=0; j=200000+v[i]; ++j)   {
                if (dp[j-v[i]] > -INF)
                    dp[j] = max (dp[j], dp[j-v[i]] + w[i]);
            }
        }
    }
    int ans = 0;
    for (int i=100000; i<=200000; ++i)  {
        if (dp[i] >= 0 && dp[i]+i-100000 > ans)
            ans = dp[i] + i - 100000;
    }
    printf ("%d
    ", ans);
    

    7. HDOJ 1203 I NEED A OFFER!

      概率问题,考虑对立事件,P (拿到offer) = 1 - P (上一次没拿到offer) * P (这次没拿到offer)

      代码:

    memset (dp, 0, sizeof (dp));
    for (int i=1; i<=m; ++i)    {
    	for (int j=n; j>=v[i]; --j) {
    		dp[j] = max (dp[j], 1 - (1 - dp[j-v[i]]) * (1 - p[i]));
    	}
    }
    printf ("%.1lf%%
    ", dp[n] * 100);
    

      

    8. HDOJ 2955 Robberies

      概率问题,与上一题类似,反向思维

      代码:

    int sum = 0;
    for (int i=1; i<=N; ++i)    {
        scanf ("%d%f", &m[i], &p[i]);
        sum += m[i];						                    //反向思维, 考虑逃脱概率
    }
    for (int i=1; i<=sum; ++i)  dp[i] = 0;
    dp[0] = 1;								                    //没偷钱逃脱的概率
    for (int i=1; i<=N; ++i)    {
        for (int j=sum; j>=m[i]; --j)	{                       //偷钱逃脱的概率
            dp[j] = max (dp[j], dp[j-m[i]] * (1 - p[i])); 		//少写了个d ->成p (/ □ )
        }
    }
    P = 1 - P;
    for (int i=sum; i>=0; --i)  {
        if (dp[i] >= P)		{                                   //寻找大于成功逃脱概率的最大偷钱数
            printf ("%d
    ", i);
            break;
        }
    }
    

      

    编译人生,运行世界!
  • 相关阅读:
    6/10
    Alpha 冲刺5/10
    现场编程
    4/10
    3/10
    总结
    bbbbbeta
    总结
    beta 7
    beta 6
  • 原文地址:https://www.cnblogs.com/Running-Time/p/4791945.html
Copyright © 2011-2022 走看看