No.1 DP 背包 POJ 1837 Balance

Balance
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 4100   Accepted: 2369

Description

Gigel has a strange "balance" and he wants to poise it. Actually, the device is different from any other ordinary balance. 
It orders two arms of negligible weight and each arm's length is 15. Some hooks are attached to these arms and Gigel wants to hang up some weights from his collection of G weights (1 <= G <= 20) knowing that these weights have distinct values in the range 1..25. Gigel may droop any weight of any hook but he is forced to use all the weights. 
Finally, Gigel managed to balance the device using the experience he gained at the National Olympiad in Informatics. Now he would like to know in how many ways the device can be balanced. 

Knowing the repartition of the hooks and the set of the weights write a program that calculates the number of possibilities to balance the device. 
It is guaranteed that will exist at least one solution for each test case at the evaluation. 

Input

The input has the following structure: 
• the first line contains the number C (2 <= C <= 20) and the number G (2 <= G <= 20); 
• the next line contains C integer numbers (these numbers are also distinct and sorted in ascending order) in the range -15..15 representing the repartition of the hooks; each number represents the position relative to the center of the balance on the X axis (when no weights are attached the device is balanced and lined up to the X axis; the absolute value of the distances represents the distance between the hook and the balance center and the sign of the numbers determines the arm of the balance to which the hook is attached: '-' for the left arm and '+' for the right arm); 
• on the next line there are G natural, distinct and sorted in ascending order numbers in the range 1..25 representing the weights' values. 

Output

The output contains the number M representing the number of possibilities to poise the balance.

Sample Input

2 4	
-2 3 
3 4 5 8

Sample Output

2

Source

Romania OI 2002

发现书看多了还是不行,要多做题!!!

DP的边界太重要了,一开开始把V_MAX值估计小了,一直WA, 仔细估算改成7500后就AC

 

(1)很早之前写的一个版本,用了递归,其实DP最好还是不要用递归

#include <iostream>
#define C_MAX 20
#define G_MAX 20
#define V_MAX 7500
using namespace std;

static int t1[C_MAX + 1];
static int t2[G_MAX + 1];

static int m[G_MAX + 1][C_MAX + 1];
static int v[G_MAX + 1][2 * V_MAX + 1];

int r_c = 0;
int r_v = 0;

int test(int num, int value) //DP主程序,采用备忘录
{
     int tempvalue = value;
     if(value < 0)
          tempvalue = V_MAX - value;
      if(v[num][tempvalue] != -1)
           return v[num][tempvalue];
      else if(num == 0 && value != 0)
      {
            v[num][tempvalue] = 0;
            return 0;
      }
      else
      {
            int total = 0;
            for(int i = 1; i <= r_c; i++)
            {
                  total += test(num - 1, value + m[r_v - num + 1][i]); //求子问题
            }
            v[num][tempvalue] = total; //备忘
            return total;
      }
}
int main()
{
     cin>>r_c>>r_v;

     //
     for(int i1 = 1; i1 <= r_c; i1++)
     {
          cin>>t1[i1];
     }
     for(int i2 = 1; i2 <= r_v; i2++)
     {
          cin>>t2[i2];
     }
     for(int i3 = 1; i3 <= r_v; i3++)
     {
          for(int i4 = 1; i4 <= r_c; i4++)
          {
               m[i3][i4] = t2[i3] * t1[i4];
          }
     }
     for(int i = 0; i <= G_MAX; i++)
     {
          for(int j = 0; j <= 2 * V_MAX; j++)
          {
               v[i][j] = -1;
          }
     }
     v[0][0] = 1;
     test(r_v, 0);
     cout<<v[r_v][0]<<endl; //输出
     return 0;
}

 

(2)非递归版本

/*

典型的覆盖型背包问题,有条件可知,总的力矩的范围是-7500 ~ 7500,为了方便处理,总体迁移7500,变成1 ~15000

然后利用背包的解法依次处理所有砝码以及放置的位置

*/

#include <iostream>
#define MAX_N 15010
#define MAX_H 20
#define MAX_W 20
using namespace std;


//背包的记录数组,利用二维滚动 can[][i]表示到当前砝码可以让总力矩达到i的方案总数

int can[2][MAX_N + 1];

//输入
int hook[MAX_H + 1], weight[MAX_W + 1];
int h_n, w_n;

int main()
{
    int i, j, w;

    //读取输入

    scanf("%d%d", &h_n, &w_n);
    for(i = 0; i < h_n; i++) scanf("%d", &hook[i]);
    for(i = 0; i < w_n; i++) scanf("%d", &weight[i]);

    //p用来指示当前滚动数组的第一维位置   

    int p = 0;

    memset(can, 0, sizeof(can));

    //背包起始状态,处于平衡状态
    can[0][7500] = 1;

    //遍历砝码

    for(i = 0; i < w_n; i++)
    {

        //遍历当前砝码可以防止的位置
        for(j = 0; j < h_n; j++)
        {

            //当前砝码防止在当前位置可以增加的力矩值
            int val = hook[j] * weight[i];

            //遍历当前背包可达状态
            for(w = 0; w <= MAX_N; w++)
            {
                if(can[p][w] != 0)
                    can[(p + 1) % 2][w + val] += can[p][w];
            }
        }
        memset(can[p], 0, sizeof(can[p]));

        //最后一个砝码防止完毕,输出结果,结果是平衡状态的可达途径总数
        if(i == w_n - 1) printf("%d/n", can[(p + 1) % 2][7500]);
        p = (p + 1) % 2;
    }
    return 0;
}

 

 

你可能感兴趣的:(No.1 DP 背包 POJ 1837 Balance)