P2340 [USACO03FALL]Cow Exhibition G題解
阿新 • • 發佈:2020-10-07
新的奇巧淫技
眾所周知,模擬退火是一種很強大的演算法,DP很強,但我模擬退火也不虛,很多題你如果不會的話基本可以拿來水很多分。比如這道題,我用模擬退火可以輕鬆水過(雖然我是足足交了兩頁才過)但是沒有什麼大問題,如果模擬退火還不會,建議先看你穀日報學習一下。剩下的主要就是一些細節,看程式碼。
#include <bits/stdc++.h> using namespace std; #define gc getchar() #define re register double st=clock(); int n,tot,ans,x,y; int I[1000000+10],E[1000000+10]; struct node{ int IQ,EQ; }a[1000000+10];//定義結構體,方便random_shuffle inline int read(){ int r=0,l=1;char ch=gc; while(!isdigit(ch)){if(ch=='-')l=-1;ch=gc;} while(isdigit(ch)){r=(r<<3)+(r<<1)+ch-'0';ch=gc;} return r*l; } inline void add(int x,int y){ if(x<0&&y<0)return;//如果兩個都是0,都答案沒有任何貢獻,直接pass if(x>=0&&y>=0) return a[++tot]=(node){x,y},void();//如果兩個都>0,那麼最優解一定會有它,直接加進去即可 if((x<0&&y>0&&abs(x)-y>=rand())||(x>0&&y<0&&abs(y)-x>=rand()))return;//如果兩個值是一正一負相差太大可以pass,但一定要注意是>rand(),這樣會有一定概率接受這個解,至於為什麼,留給讀者思考 if(x+y<0)if(exp(-(x+y))>(double)rand()/(double)RAND_MAX)return;//如果兩個值相加為0,對答案會有負貢獻,但仍要有一定機率接受,好像和上面判重了,因為本人太菜,不知道去掉對不對,所以就寫上了~ a[++tot]=(node){x,y}; } inline void solve(){ tot=0;for(re int i=1;i<=n;++i)add(I[i],E[i]);//初始化,重新選取合適牛 random_shuffle(a+1,a+tot+1);//打亂重排 x=0,y=0; for(re int i=1;i<=tot;++i){ x+=a[i].IQ;y+=a[i].EQ; if(x<=0&&y<=0)//到此為止,答案已經不合法 if(exp(ans-x-y)>(double)rand()/(double)RAND_MAX)return;//運用模擬退火思想,雖然已經不合法,但考慮後面可能有解加上後就合法,所以有一定概率接受這個解並繼續。這也是模擬退火演算法的優勢 if(x>0&&y>0)ans=max(ans,x+y);//合法的話,更新答案 } } int main(){ srand(time(NULL)); n=read(); for(re int i=1;i<=n;++i)I[i]=read(),E[i]=read();//存取各頭牛的IQ和EQ,因為上方add時會有刪除,因為是隨機刪除,可能會造成第一次就把最優解中的一頭牛刪除,然後就掛了,上面我的64分就是這樣錯的 int crl=15000; while(crl--)solve(); printf("%d\n",ans); return 0; }