Sweety

Practice makes perfect

导航

湖南多校对抗赛---Rectangle(01背包)

Posted on 2015-03-29 08:45  蓝空  阅读(188)  评论(0编辑  收藏  举报

 题目网址:http://acm.csu.edu.cn/OnlineJudge/problem.php?cid=2071&pid=0

Description

Now ,there are some rectangles. The area of these rectangles is 1* x or 2 * x ,and now you need find a big enough rectangle( 2 * m) so that you can put all rectangles into it(these rectangles can't rotate). please calculate the minimum m satisfy the condition.

Input

There are some tests ,the first line give you the test number.
Each test will give you a number n (1<=n<=100)show the rectangles number .The following n rows , each row will give you tow number a and b. (a = 1 or 2 , 1<=b<=100).

Output

Each test you will output the minimum number m to fill all these rectangles.

Sample Input

2
3
1 2
2 2
2 3
3
1 2
1 2
1 3

Sample Output

7
4
 

解题Report:

显然题意很简单,就是找出能盛下所有矩形的最小的大矩形(注意条件小矩形的宽为1或2,大矩形的宽为2),另外不能够旋转(开始的时候题意根本没有说清楚,以为小的不能旋转,但是大的能够旋转,吃了很大的亏,因为这样的话1,2    2,2     2,2这样的测试数据需要把前面的数相加,也就是应该是5,但是事实证明这样时候是想多了)

其实刚拿到题的时候就把这道题归为了01背包的问题,就是小矩形的宽为2的直接相加,剩下的为1的就放在背包里就行了,也就是说从宽度小于1的和的一半(注意奇偶性)开始遍历,每次把背包的容量加1,当背包正好装满的时候这就是最小的长度,所以很简单喽

开始的时候一直没有A掉,就是因为没有初始化,第一次背包没有装满,当第二次的时候就直接用上一次的那个数组了,这样肯定是不对的,然后想了好多的边界条件,结果是越改越错,越改越不对了,这个可恨的错还是比赛之后查出来的,当时A的用的是第二种大哭大哭大哭。。。


代码:

 #include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
	int n,count=0,tmp=0,vol[1001],f[10050]={0},sum=0;
	int a,b;
	cin>>n;
    while(n--){
    	cin>>a>>b;
    	if(a==2) count+=b;
    	else {
		  vol[tmp++]=b;
		  sum+=b;
    	}
    } 
    int xx=(sum%2==0?sum/2:sum/2+1);
	for(int v=xx;v<=sum;v++){
		memset(f,0,sizeof(f));
		for(int i = 0 ; i < tmp ; i++)  //便利i件物品
        {
            for(int j = v ; j >= vol[i]; j--)
            {
                int tem = f[  j-vol[i]  ] + vol[i];
                if( f[j] < tem )
                f[j] = tem;
            }
        }
        if(f[v]==v) 
		{
        cout<<count+v;
		if(T!=0) cout<<endl;
        break;
		}
	} 
}
 return 0;
}

没有办法,受人启发,WR了好几次只好稍微改了下思路,但是动规的思路没有改,也就是在看背包的时候直接用和的一半做背包的容积,运用01背包的方式,找出能盛下的最大的体积,然后和剩下的长度相比,哪个大就输出哪个(别忘了加上宽度为2的长度),很明显这种做法更加巧妙,按理来说也速度应该更快,但是这道题的数据量不是很大,所以没有明显的作用。

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
    int n,count=0,tmp=0,vol[1001],f[10050]={0},sum=0;
    int a,b,mark=0;
    cin>>n;
    while(n--){
        cin>>a>>b;
        if(a==2){
        count+=b;
        }
        else {
          vol[tmp++]=b;
          sum+=b;
        }
    }
    int v=sum/2; 
    for(int i = 0 ; i < tmp ; i++)  //遍历i件物品
    {
       for(int j = v ; j >= vol[i]; j--)
       {
         int tem = f[  j-vol[i]  ] + vol[i];
         if( f[j] < tem )
            f[j] = tem;
       }
    }
    cout<<(sum-f[v]>f[v]?count+sum-f[v]:count+f[v])<<endl;
}
 return 0;
}
 

其实这道题给我的最大的启发根本不是这道题的知识点,因为背包早就做过,而是每道题的解题方式和方法,其实在ACM这类题中很少有在格式上很严格(一些特殊题型如字符串处理除外),重在算法,所以说一道题没有AC掉的话首先应该想到的是算法的问题(当然这是在边界条件考虑之后的),当时做这道题最大的缺点就是太过相信写的算法了,一心在找边界条件,而忽略了这一点,以后要注意了。。。