题目网址: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掉的话首先应该想到的是算法的问题(当然这是在边界条件考虑之后的),当时做这道题最大的缺点就是太过相信写的算法了,一心在找边界条件,而忽略了这一点,以后要注意了。。。