1. 程式人生 > >robin哥哥:資料範圍小的數是題目的突破點

robin哥哥:資料範圍小的數是題目的突破點

XOR Clique

題目:
寶寶現在有一個序列a1,a2……an,它想找一個子序列S,使得子序列裡面的任意兩個數異或之後小於兩數的最小值,且該子序列要最長,輸出最長子序列長度
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
分析:兩數異或,同數位如果相同則為0,不同則為1,一個十進位制的數轉換為二進位制數之後最高位一定為1,如果兩個十進位制數轉換為二進位制數之後長度相同,那麼最高位異或之後就會是0,所以要想實現子序列中任意兩數異或之後小於兩數最小的那個,就要保證子序列中所有數轉換為二進位制數之後長度相同。那麼問題就變為:把序列中每個數轉換成二進位制數的長度存到雜湊表,再在雜湊表中找最大值,即為最長子序列。

#include <bits/stdc++.h>
using namespace std;
int a[100005];
int main()
{
    int power[33],t,i,n;
    scanf("%d",&t);
    while(t--)
    {
        memset(power,0,sizeof(power));//別忘了每個case清空雜湊表
        scanf("%d",&n);
        for(i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            power[(int)log2(a[i])+1]++;//計算每個數轉換為二進位制數的長度,並存到雜湊表裡
        }
        int maxn=0;//別忘了每次都要令maxn歸零
        for(i=0; i<=32; i++)
        {
            maxn = max(maxn,power[i]);//找最長子序列
        }
        printf("%d\n",maxn);
    }
}

Coins and Queries

題目:
Polycarp有n個硬幣,第i個硬幣的價值是ai。 保證所有值都是2的整數冪(即對於某些非負整數d,ai = 2^d)。
Polycarp希望知道q查詢的答案。 第j個查詢被描述為整數bj。 查詢的答案是使用某些硬幣子集獲得值bj所需的最小硬幣數量(Polycarp只能使用他擁有的硬幣)。 如果Polycarp無法獲得值bj,則第j個查詢的答案為-1。
查詢是獨立的(查詢的答案不影響Polycarp的硬幣)。
在這裡插入圖片描述
在這裡插入圖片描述
分析:題目中說硬幣的面值都是2 ^ d(d為非負數),又知道任何一個2 ^ d的數都可以由2的更低次冪相加得到,那麼很明顯暗示我們要把每枚硬幣的d算出來,放進數位雜湊表,然後從最高次冪開始貪心算下來,如果最後查詢數值可以變為0,則說明能湊出這個數,反之,則不能。

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
   int n,q,i,a,power[32]={0},c,j,k,b;
   scanf("%d%d",&n,&q);
   for(i=0; i<n; i++)
   {
       scanf("%d",&a);
       power[int(log2(a)]++;//把每枚硬幣的d算出來,存進雜湊表
   }
   for(i=0; i<q; i++)
   {
       scanf("%d",&b);
       c = 0;//計數器
       for(j=31; j>=0; j--)//硬幣最大面值不會超過2^31次方,所以從31開始列舉
       {
          k = min(power[j],b/(1<<j));//1<<j表示2^j,k是應該拿出該面值的硬幣的個數,理想是有多少拿多少,但是有可能b並沒有那麼大,所以k=min(該面值硬幣個數,最大能容納的個數)
          c += k;
          b -= k*(1<<j);//一共拿了多少錢,總數就得減去多少
       }
       if(b != 0)//當硬幣面值都列舉完,發現b還是沒能為0,那麼就說明這堆硬幣湊不出b數值的數
            printf("-1");
       else
            printf("%d",c);
       printf("\n");
   }
}