Uva12325 Zombie's Treasure Chest [二分区间+模拟退火]
Zombie’s Treasure Chest
題目鏈接
https://cn.vjudge.net/problem/UVA-12325
題意
兩種物品無窮多個,第一種物品重量s1s_1s1?,價值v1v_1v1?,第二種物品重量s2s_2s2?,價值v2v_2v2?,背包重nnn,求能裝的最大價值之和. 數據全都是2e92e92e9.也就是兩種物品的完全背包.
題解
不可思議吧,這題還能模擬退火?
但仔細一想,求解最優值,而且隨機解的生成也很簡單,當然可以嗎,模擬退火搞啦.
模擬退火的板子可以到我以前的博客里找到,現在默認大家都知道模擬退火怎么寫了.
這道題我雖然用模擬退火AAA掉了,但也嘗試了好多發,現在把我采坑的過程根大家分享一下.
嘗試一
直接套板子,設xxx為第一種物品取的個數,顯然x∈[0,?ns1?]x \in [0,\lfloor \frac{n}{s_1} \rfloor]x∈[0,?s1?n??],那么第二種物品的個數就是y=?n?s1?v1s2?y =\lfloor \frac{n-s_1*v_1}{s_2} \rfloory=?s2?n?s1??v1???.
因此模擬退火的時候我可以在區間[0,?ns1?][0,\lfloor \frac{n}{s_1} \rfloor][0,?s1?n??]中隨機一個數作為xxx,然后計算yyy,并且計算能量值E=x?v1+y?v2E = x*v_1+y*v_2E=x?v1?+y?v2?.
最后調調參數,使得平衡一下答案精度和時間復雜度.
嘗試結果
多次嘗試以后,一直WA,自己造了組極端數據2000000000,2,3,1,12000000000,2,3,1,12000000000,2,3,1,1,發現根本過不去,總結原因:當隨機區間過大的時候,很難隨機到正確解,所以算法就在某個半山腰停住了.
嘗試二
要想能想要枚舉到最優解,區間一定不能太大.我們可以分塊進行模擬退火,這樣可以保證每次隨機的區間不會太大,區間上的某一個點被隨機到的概率就更大了,這種做法我還沒有試過,但是感覺應該可行.我們進一步發現,這個函數的峰不會太多(實際沒幾個)大致是具有單調性質的,因此我們采用二分區間的做法,即對于當前區間,用模擬退火算出一個最優解,然后用這個解與區間中點做比較從而確定下一個需要進行模擬退火的區間.
通過多次調參之后:
代碼
#include <iostream> #include <algorithm> #include <cmath> #include <ctime> #include <cstring> int T,cas; long long n,s1,v1,s2,v2; double randfloat() {return rand()/(RAND_MAX+0.0); } void solve() {std::cin >> n >> s1 >> v1 >> s2 >> v2;long long ansE = 0,ansx = 0;long long nowE = 0,x = 0;long long low = 0,up = n/s1;for(int cc = 1;cc <= 20;++cc) {double T0 = 1000000,Tk = 1,T = T0,d = 0.999;while(T > Tk) {long long newx = low + rand()%(up-low+1);long long newE = newx * v1 + ((n-newx*s1)/s2)*v2;if(newE < nowE || randfloat() > exp((nowE-newE)/T)) {nowE = newE;x = newx;}T *= d;if(newE > ansE) {ansE = newE;ansx = newx;}}long long mid = (low + up)/2;if(ansx > mid) low = mid;else up = mid;}std::cout << "Case #" << ++cas << ": " << ansE << std::endl; } int main() {std::ios::sync_with_stdio(false);std::cin >> T;while(T--) solve();return 0; }總結
以上是生活随笔為你收集整理的Uva12325 Zombie's Treasure Chest [二分区间+模拟退火]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 织梦(dedecms)利用sql语句怎么
- 下一篇: P2414 NOI2011阿狸的打字机