1. 程式人生 > >TJUOJ4168解題報告

TJUOJ4168解題報告

too name num 二進制位 如果 sin algo sign pac

題目地址:

  http://acm.tju.edu.cn/toj/showp4168.html

題目概述:

  給出一個集合以及一個m,集合中一開始有n個數,要求加入最少的數使得這個集合的各個子集的和能夠覆蓋1~m。

  例如集合A={1,3},m=6,這時只需要將2加入A中,A的所有子集為[1], [2], [3], [1,2], [1,3], [2,3], [1,2,3],他們的和分別是1,2,3,3,4,5,6,能夠覆蓋1~6這個區間,所以答案為1。

大致思路:

  一開始想到二進制位上去了,覺得每個數的二進制位都有個貢獻之類的,結果還是too naive啊,跟正解差的太多。

  首先對子集A的元素排個序,然後假設當前子集中最大的數為a[i-1],能夠覆蓋的範圍是1~mx,那麽如果a[i]>mx+1的話,必須要插入mx+1這個數才能使覆蓋的範圍仍舊連續,而插入mx+1,會使得覆蓋的範圍變成2*mx+1,此時再比較a[i]與當前的mx的大小,直到a[i]<=mx+1時加入a[i]會使得覆蓋的範圍變成mx+a[i],一直這麽考慮下去就ok。

復雜度分析:

  首先對於數m來說,最多只需要log2(m)個數就可以滿足題意了,那麽插入的復雜度應該就是O(n+logm),之前還需要排序,就是O(nlogn),總的的復雜度即O(nlogn)了。

代碼:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <ctime>
#include <map>
#include <assert.h>
#include 
<stack> #include <set> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define sacnf scanf #define scnaf scanf #define maxn 100010 #define maxm 20010 #define inf 1061109567 #define INF 0x3f3f3f3f #define Eps 0.000001 const double PI=acos(-1.0); #define mod 1000000007 #define
MAXNUM 10000 #define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++) #define mes(a,b) memset((a),(b),sizeof(a)) typedef long long ll; typedef unsigned long long ulld; void Swap(int &a,int &b) {int t=a;a=b;b=t;} ll Abs(ll x) {return (x<0)?-x:x;} int a[maxn]; int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); //clock_t st=clock(); int T;scanf("%d",&T); while(T--) { int n;ll m;scanf("%d%lld",&n,&m); For(i,1,n) scanf("%d",&a[i]); sort(a+1,a+1+n);ll ms=0;int cnt=0; for(int i=1;i<=n;i++) { if(ms>=m) break; while(a[i]>ms+1&&ms<m) cnt++,ms=ms*2+1; ms=ms+a[i]; } while(ms<m) cnt++,ms=ms*2+1; printf("%d\n",cnt); } //clock_t ed=clock(); //printf("\n\nTime Used : %.5lf Ms.\n",(double)(ed-st)/CLOCKS_PER_SEC); return 0; }

TJUOJ4168解題報告