CF1303D 位运算+贪心

CF1303D

题目大意:

有一个大小为n的背包,有m个大小为2i(i=0,1,2,3...)的物品,并且每个物品可以分成大小相同的两份,可以一直分成到大小为1为止,问能否用这些物品把背包恰好填满,如果能填满,输出拆分物品的最小次数。

解题思路:

通过二进制的方法,从高位到低位贪心的一位一位处理。

比如样例一:

10  3

1   32   1

n=10,转换成二进制为1010,所以我们看成是1000+10.

三个物品转换成二进制分别为:1   100000  1

那么我们用一个dp[i]来表示第i位上有多少个1,比如上述的三个物品转换成dp数组就是dp[0]=2,dp[5]=1,其余都为0。

那么我们只需要从n对应的二进制的第0位开始与dp[]进行比较,如果dp[i]上的数量可以满足n的要求,则直接减去,否则就需要拆分较大的物品,即为向上借位。

具体可以看代码及其注释

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <algorithm>
 5 #include <queue>
 6 #include <stack>
 7 #include <stdio.h>
 8 #include <cmath>
 9 #include <string.h>
10 
11 using namespace std;
12 #define ll long long
13 static const int WHITE=0;
14 static const int GRAY=1;
15 static const int BLACK=2;
16 static const int INF=0x3f3f3f3f;
17 ll Pow(ll a,ll b,ll mod){if(b==0) return 1%mod; ll sum=1; a=a%mod; while(b>0) { if(b%2==1) sum=(sum*a)%mod; b/=2; a=(a*a)%mod;}return sum;}
18 int dp[35];//用来记录第i位上有多少个1
19 void change(int a)//由于物品的大小都是2的n次方,所以其二进制一定为1000---的形式
20 {
21     int k=0;
22     while((a>>k)>1)//找到该数的二进制的1在第几位
23     k++;
24     dp[k]++;
25 }
26 int main()
27 {
28     freopen("C:\\Users\\16599\\Desktop\\in.txt","r",stdin);
29     int T;
30     cin>>T;
31     while (T--)
32     {
33         memset(dp,0,sizeof(dp));
34         ll n,m,sum;
35         sum=0;
36         cin>>n>>m;
37         for(int i=1;i<=m;i++)
38         {
39             int a;
40             cin>>a;
41             sum+=a;
42             change(a);
43         }
44         if(sum<n)//全部装进都装不满
45         {
46             cout<<"-1"<<endl;
47             continue;
48         }
49         int p=0,num=0;
50         while(n||dp[p]<0)
51         {
52             dp[p]-=n&1;//n&1是用来判断n转换成二进制后的最低位是否为1
53             if(dp[p]<0)//如果物品的对应位置不能满足n的需求,则需要向高位借位
54             {
55                 dp[p+1]--;
56                 num++;//较大物品拆分,次数加一
57             }
58             else
59             dp[p+1]+=dp[p]/2;//如果低位有的多,可以给高位用
60             n>>1;//n的最低为处理完成,进一位
61             p++;
62         }
63         cout<<num<<endl;
64     }
65     
66     return 0;
67 }
View Code

 

posted @ 2020-04-27 08:38  Linkss  阅读(212)  评论(0编辑  收藏  举报