[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;
}



                 

 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值