[Codeforces 814D] An overnight dance in discotheque 树形dp,贪心
題目鏈接
題解:這道題,首先可以發現,圓與圓關系只有內含與外離,
??????????? 所以可以建立出一個樹形結構,
??????????? 每個圓的父親是與這個圓半徑相差最小且包含這個圓的圓,
??????????? 這樣,整個一張圖形成了一個森林,可以將圓按半徑排序后O(n^2)建立出來。
Solution 1 :? 可以對這個森林進行樹形dp,
????????????????????? 因為需要將森林分成兩個部分,
????????????????????? 而且容易發現,對于新形成的森林,深度為奇數的圓貢獻為S,偶數為-S,
????????????????????? 于是對于每棵樹分別dp,
????????????????????? f[i][0/1][0/1]表示以i為根,2個01變量分別表示到達i時兩棵樹的深度的奇偶性,
????????????????????? 顯然,因為是樹上深度相關,這兩個01變量應該從根到兒子進行轉移,
????????????????????? 通過枚舉在i位置的選擇,dp數組的求解由兒子到根進行轉移。
????????????????????? (這么設狀態可以保證兒子對父親的貢獻總是為正,如果兒子對父親貢獻出現負值,就不符合dp的最優子結構性質了)
????????????????????? (菜雞博主開始設置的狀態會使兒子對父親的貢獻可能為負,所以導致比賽時GG?? 我好菜啊高清重置版)
Code:
#include <bits/stdc++.h> #define eps 1e-12 double pi=acos(-1.0); using namespace std; struct node {double x,y,r; }t[1005]; int n; int fa[1005]; double f[1005][2][2]; vector <int> vec[1005]; inline bool cmp(node a,node b) {return a.r>b.r;} void dfs(int pos) {if (vec[pos].size()==0) {f[pos][0][0]=-t[pos].r*t[pos].r*pi; f[pos][1][1]=t[pos].r*t[pos].r*pi; f[pos][0][1]=t[pos].r*t[pos].r*pi; f[pos][1][0]=t[pos].r*t[pos].r*pi; return; } /* f[pos][0][0]=-t[pos].r*t[pos].r*pi; f[pos][0][1]=-t[pos].r*t[pos].r*pi; f[pos][1][0]=t[pos].r*t[pos].r*pi; f[pos][1][1]=t[pos].r*t[pos].r*pi; f[pos][0][0]=-t[pos].r*t[pos].r*pi; f[pos][0][1]=t[pos].r*t[pos].r*pi; f[pos][1][0]=-t[pos].r*t[pos].r*pi; f[pos][1][1]=t[pos].r*t[pos].r*pi; */ double s[2][2]; memset (s,0,sizeof(s)); for (int i=0;i<vec[pos].size();i++) {dfs(vec[pos][i]); int son=vec[pos][i]; s[0][0]+=f[son][0][0]; s[0][1]+=f[son][0][1]; s[1][0]+=f[son][1][0]; s[1][1]+=f[son][1][1]; } double siz=t[pos].r*t[pos].r*pi; f[pos][0][0]=max(s[0][1]-siz,s[1][0]-siz); f[pos][1][0]=max(s[0][0]+siz,s[1][1]-siz); f[pos][1][1]=max(s[0][1]+siz,s[1][0]+siz); f[pos][0][1]=max(s[0][0]+siz,s[1][1]-siz); } int main (){int i,j;scanf ("%d",&n);for (i=1;i<=n;i++){scanf ("%lf%lf%lf",&t[i].x,&t[i].y,&t[i].r);}sort(t+1,t+n+1,cmp);for (i=1;i<=n;i++){for (j=i-1;j>=1;j--){if (((t[i].x-t[i].r)+eps>t[j].x-t[j].r)&&((t[i].x+t[i].r)-eps<t[j].x+t[j].r)&&((t[i].y-t[i].r)+eps>t[j].y-t[j].r)&&((t[i].y+t[i].r)-eps<t[j].y+t[j].r)){vec[j].push_back(i);fa[i]=j;break;}}}double ans=0;for (i=1;i<=n;i++){if (!fa[i]){dfs(i);double maxn=-1e18;ans+=f[i][1][1];}}printf ("%.12lf\n",ans);return 0; }Solution 2:? 非常妙的貪心,建出樹之后,????????? ?????? ? ?
對于樹上某個位置i,只要能夠使它的貢獻為正,就讓它貢獻為正數。??????? ? ? ? ? ? ??
正確性:對于每個圓,若使它貢獻為正,與讓它貢獻為負時相比,答案相差了2*Si.?????? ? ? ? ? ?? ??
而對于這個圓所對應的子樹的選擇,整個子樹對于答案的貢獻不會小于-Si,也不會大于Si,?????????????????? ?
所以顯然這種情況下讓這個圓貢獻為正所取得的收益更大,所以貪心是正確的。
Code:
#include <bits/stdc++.h> #define eps 1e-12 double pi=acos(-1.0); using namespace std; struct node {double x,y,r; }t[1005]; inline bool cmp(node a,node b) {return a.r>b.r;} int n; int fa[1005],dep[1005]; int main (){int i,j;scanf ("%d",&n);for (i=1;i<=n;i++){scanf ("%lf%lf%lf",&t[i].x,&t[i].y,&t[i].r);}sort(t+1,t+n+1,cmp);for (i=1;i<=n;i++){for (j=i-1;j>=1;j--){if (((t[i].x-t[i].r)+eps>t[j].x-t[j].r)&&((t[i].x+t[i].r)-eps<t[j].x+t[j].r)&&((t[i].y-t[i].r)+eps>t[j].y-t[j].r)&&((t[i].y+t[i].r)-eps<t[j].y+t[j].r)){fa[i]=j;break;}}}double ans=0;dep[0]=0;for (i=1;i<=n;i++){dep[i]=dep[fa[i]]+1;}for (i=1;i<=n;i++){if (dep[i]<=2||dep[i]%2==0) {ans+=pi*t[i].r*t[i].r;}else {ans-=pi*t[i].r*t[i].r;}}printf ("%.12lf\n",ans);return 0; }
總結
以上是生活随笔為你收集整理的[Codeforces 814D] An overnight dance in discotheque 树形dp,贪心的全部內容,希望文章能夠幫你解決所遇到的問題。