日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P1582 倒水(二进制)

發布時間:2024/9/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P1582 倒水(二进制) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://www.luogu.org/problemnew/show/P1582
P1582 倒水
評測方式 云端評測
標簽
難度 普及+/提高
時空限制 1000ms / 128MB

提示:收藏到任務計劃后,可在首頁查看。

最新討論
推薦的相關題目
題目描述
一天,CC買了N個容量可以認為是無限大的瓶子,開始時每個瓶子里有1升水。接著~~CC發現瓶子實在太多了,于是他決定保留不超過K個瓶子。每次他選擇兩個當前含水量相同的瓶子,把一個瓶子的水全部倒進另一個里,然后把空瓶丟棄。(不能丟棄有水的瓶子)

顯然在某些情況下CC無法達到目標,比如N=3,K=1。此時CC會重新買一些新的瓶子(新瓶子容量無限,開始時有1升水),以到達目標。

現在CC想知道,最少需要買多少新瓶子才能達到目標呢?

輸入輸出格式
輸入格式:
一行兩個正整數, N,K(1\le N\le 2\times 10^9,K\le 10001≤N≤2×10
9
,K≤1000)。

輸出格式:
一個非負整數,表示最少需要買多少新瓶子。

輸入輸出樣例
輸入樣例#1:

3 1

輸出樣例#1:

1

輸入樣例#2:

13 2

輸出樣例#2:

3

輸入樣例#3:

1000000 5

輸出樣例#3:

15808

ac_code:
/*
這題沒有提高難度,就是普及吧~
實質是二進制的應用吧
/
自己是這樣做的
/
補充一個解釋:
A:瓶子先水量相同的兩兩合并,如果是一開始n為奇數,肯定會剩下一份不能兩兩合并的,如果是偶數,就剛好兩兩合并,不會剩下。如果剩下一份,這一份的水量 肯定是這次能合并的任意一份水量的 1/2。
經過兩兩合并后,瓶子剩下的個數為n/2,除了那個不能合并的(水量不變),每個瓶子中的水量為之前的兩倍,
對剩下的水量相同的繼續兩兩合并,即重復A。。
經過多次A操作后,最后沒有能兩兩相同水量的合并了,即現在剩下的都是每次A操作剩下的不能進行兩兩相同合并的瓶子,如果此時瓶子個數小于等于m則不需要買瓶子,否則,這樣買:
先買第一次A操作與第二次A操作剩下那個瓶子的水量的差值個瓶子,使它又可以兩兩合并,再直到不能兩兩合并,重復這個購買操作,直到剩下的瓶子小于等于m
舉個例子:
n = 7 m = 2
第一次A:7/ 2 = 3 ,7 % 2 = 1(剩下這個不能合并的瓶子水量為1)
第二次A: 3 / 2 = 1 ,3 % 2 = 1(剩下這個不能合并的瓶子水量為 2)
此時不能進行A操作了,剩下3個瓶子,瓶子水量分別為 :4 ,2 ,1
此時我們要先買1個瓶子,這時:4 ,2,1,1
又可以兩兩合并:
4 ,2 ,2
4 ,4(此時已經滿足條件)
8(當然購買這次后可以最終使它剩下1個水量為8的瓶子)
最后可以只剩下1個瓶子,所以買1個瓶子就可以了
(我們最初的目的是要先讓已有的瓶子經過合并后盡可能的少,所以一直重復A操作,直到不能再進行A操作)
*/
way1:

#include <iostream> using namespace std; typedef long long LL; LL q[50]; int main() {LL n,m;cin>>n>>m;LL ans = 0,x = n,y = 0,w = 1,k = 0;while(x){y += x&1; //y記錄最終剩下的瓶子數量(奇數就會有剩余)if(x&1) q[++k] = w; //如果每趟合并有剩余,那個剩余的瓶子水量x >>= 1; w <<= 1;}LL head = 1,tail = k,len = y;while(head < tail && len > m){ans += q[head+1] - q[head]; //每次需要購買的數量q[head] = q[head+1];while(q[head] == q[head+1] && head < tail && len > m) //購買后繼續進行兩兩合并{q[head+1] = q[head]<<1;head++;len--; //一對兩兩合并,瓶子-1}}cout<<ans<<endl;return 0; }

做完之后去看題解
發現還可以寫的更簡單微妙些

其實根據我自己寫的那份代碼也可以發現其中的奧秘所在:
n二進制1的個數,為最初可以剩下的最少個數
之后,n += (n & -n)
//相當于使n的二進制最后一個1上加1,產生進位(連續的1變為0),
但是對于十進制,瓶子加的個數肯定是(n & -n),所以ans += (n & -n),
直到n的二進制1個數不超過m

way2:
//bitset,lowbit

#include <iostream> #include <bitset> using namespace std; typedef long long LL; int main() {LL n,m;cin>>n>>m;LL ans = 0;bitset<35>b;b = n;while(b.count() > m){ans += n&-n; n += n&-n;b = n;}cout<<ans<<endl;return 0; }

way3:
//__builtin_popcount lowbit

#include <iostream> using namespace std; typedef long long LL; int main() {LL n,m;cin>>n>>m;LL ans = 0;while(__builtin_popcount(n)>m){ans += n&-n;n += n&-n;}cout<<ans<<endl;return 0; }

/*
個人覺得沒有必要知道gcc里的__builtin_…
(如果實在想了解gcc里的__builtin_…點這里吧:gcc里的__builtin_…傳送門)
因為bitset差不多可以完全取得它,而且功能更加強大同時也更好記函數形式更好理解
(用bitset好像效率也更高的樣子,提交是快了兩毫秒)
了解bitset的用法點這里:bitset傳送門
*/

總結

以上是生活随笔為你收集整理的P1582 倒水(二进制)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。