HDU 5616 Jam's balance (折半枚举or正反背包)


Jam's balance

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2073    Accepted Submission(s): 851


Problem Description
Jim has a balance and N weights.  (1N20)
The balance can only tell whether things on different side are the same weight.
Weights can be put on left side or right side arbitrarily.
Please tell whether the balance can measure an object of weight M.
 

Input
The first line is a integer  T(1T5), means T test cases.
For each test case :
The first line is  N, means the number of weights.
The second line are  N number, i'th number  wi(1wi100) means the i'th weight's weight is  wi.
The third line is a number  M M is the weight of the object being measured.
 

Output
You should output the "YES"or"NO".
 

Sample Input
 
   
1 2 1 4 3 2 4 5
 

Sample Output
 
   
NO YES YES
Hint
For the Case 1:Put the 4 weight alone For the Case 2:Put the 4 weight and 1 weight on both side
 

Source
BestCoder Round #70
 

Recommend
hujie
 

Statistic |  Submit |  Discuss |  Note

思路:可以正反背包,正着做一遍背包代表如果全部都放在左/右面位置,可以凑出的所有值, 然后就是左面放一点右面放一点了,假如第一遍做的背包都在左面, 那么这次背包就是放在右面,可以这样理解, 左面可以凑出的状态, 我放在右面一个砝码,就相当于左面-砝码的重量,所以枚举的时候左面能凑出来,那么 左面-w[i]一定能凑出来, 加入凑成左面的里面有w[i]并不影响,如果没有, 那就又能凑出一种新的了;

正反背包代码:

#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 2e3 + 5;
int dp[maxn], w[50];
int main()
{
    int t, n;
    cin >> t;
    while(t--)
    {
        memset(dp, 0, sizeof(dp));
        cin >> n;
        for(int i = 1; i <= n; i++)
            scanf("%d", &w[i]);
        dp[0] = 1;
        for(int i = 1; i <= n; i++)
        {
            for(int j = maxn-1; j >= w[i]; j--)
                dp[j] |= dp[j-w[i]];
        }
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j < maxn-w[i]; j++)  //这里一定要正着枚举, 因为用的要是前面的一轮的状态, 如果倒着就用到这一轮了
                dp[j] |= dp[j+w[i]];
        }
        int m;
        cin >> m;
        while(m--)
        {
            int x;
            scanf("%d", &x);
            puts(dp[x] ? "YES" : "NO");
        }
    }
    return 0;
}

折半枚举代码:如果砝码很大的话, 背包做不了了, 但是折半还是能做的

#include   
using namespace std;  
const int maxn=31;  
int a[maxn];  
sets1,s2,temp;  
set::iterator it;  
int main()  
{  
    int t,n,q,x;  
    scanf("%d",&t);  
    while(t--){  
        s1.clear();s2.clear();  
        s1.insert(0);s2.insert(0);  
        scanf("%d",&n);  
        for(int i=0;i=0)    //放两边
                    temp.insert((*it)-a[i]);  
                else temp.insert(a[i]-(*it));  
            }  
            for(it=temp.begin();it!=temp.end();it++)  //所有可行状态, 一共最多3^(n/2),放左面 放右面,不放
                s1.insert((*it));  
        }  
  
        for(int i=n/2;i=0)  
                    temp.insert((*it)-a[i]);  
                else temp.insert(a[i]-(*it));  
            }  
            for(it=temp.begin();it!=temp.end();it++)  
                s2.insert((*it));  
        }  
  
        scanf("%d",&q);  
        while(q--){  
            scanf("%d",&x);  
            if(x>2000){  
                puts("NO");  
                continue;  
            }  
            int f=0;  
            for(int i=0;i<=x;i++){  
                if(s1.find(i)!=s1.end()&&s2.find(x-i)!=s2.end()){  
                    f=1;  
                    break;  
                }  
            }  
            if(f){  
                puts("YES");  
                continue;  
            }  
            for(int i=0;i<=2000;i++){  
                if((s1.find(i)!=s1.end()&&s2.find(i+x)!=s2.end())  
                ||(s2.find(i)!=s2.end()&&s1.find(i+x)!=s1.end())){  
                    f=1;  
                    break;  
                }  
            }  
            if(f)  
                puts("YES");  
            else puts("NO");  
        }  
    }  
    return 0;  
}  
  




你可能感兴趣的:(ACM技巧,背包,ACM)