Luogu P7276 送给好友的礼物 题解

P7276 送给好友的礼物

我们充分发扬人类智慧。

首先分析性质,如果一个有草莓的节点子树中存在有草莓的节点,那么我们就直接不管这个有草莓的节点,因为首先子树内的草莓已经会遍历这个点。

另外,根据 P3320 [SDOI2015] 寻宝游戏 的结论,一个人肯定是按照 DFS 序大小遍历它需要遍历的草莓节点。

注意到 n451,考虑模拟退火。每次随机交换遍历草莓的顺序,再把序列划分为前后两个集合分别给两个人走,取最大值,可以得到 77 分。

注意到这么做是极其不合理的,因为排序之后对答案影响较大的是每个点的 DFS 序而不是如何分配,于是我们模拟退火随机打乱出边,就可以获得 100 分的好成绩。

#include <bits/stdc++.h>
using namespace std;
struct val
{
	int v,p;
}w[500];
int n,m,u,v,h[500],a[500],c[500],f[500],b[500],d[500][500],dfn[500],ans=1e9,cnt=0,dfc=0;
vector<int>s[500],tmp[500];
mt19937 rd(time(0));
bool cmp(struct val a,struct val b)
{
	return a.p<b.p;
}

void dfs1(int x,int fa)
{
	for(int i=0;i<s[x].size();i++)
	    if(s[x][i]!=fa)dfs1(s[x][i],x),c[x]|=c[s[x][i]],f[x]|=c[s[x][i]];
}

void dfs2(int x,int fa,vector<int>s[])
{
	dfn[x]=++dfc;
	for(int i=0;i<s[x].size();i++)
	    if(s[x][i]!=fa)dfs2(s[x][i],x,s);
}

int cal(vector<int>s[])
{
	dfc=0,dfs2(1,0,s);
	for(int i=1;i<=m;i++)w[i].v=a[i],w[i].p=dfn[a[i]];
	sort(w+1,w+m+1,cmp);
	int la=0,ra=d[1][w[1].v]+d[w[m].v][1],mi=1e9,pr=1e9;
	for(int i=1;i<m;i++)ra+=d[w[i].v][w[i+1].v];
	mi=ra;
	for(int i=1;i<m;i++)
	    {
	    la=d[1][w[1].v]+d[w[i].v][1];
	    for(int j=1;j<i;j++)la+=d[w[j].v][w[j+1].v];
	    ra=d[1][w[i+1].v]+d[w[m].v][1];
	    for(int j=i+1;j<m;j++)ra+=d[w[j].v][w[j+1].v];
	    if(max(la,ra)>pr)break;
	    mi=min(mi,max(la,ra)),pr=max(la,ra);
	    }
	return mi;
}

void sa()
{
	double t=2e5,k=0.99;
	while(t>=1e-5)
	    {
	    	if((double)clock()/CLOCKS_PER_SEC>3.5)return;
	    	int c=t/1000.0*(double)m+1,del=0,now=0;
	    	for(int i=1;i<=n;i++)tmp[i]=s[i];
	    	for(int i=1;i<=c;i++)
	    	    {
	    	    int x=rd()%n+1;
			    shuffle(s[x].begin(),s[x].end(),rd);
			    }
	    	now=cal(s),del=now-cal(tmp),ans=min(ans,now);
			if(del>=0)
			  {
			   double p=exp(-del/t);
			   if((double)rd()/UINT_MAX>=p)
			      for(int i=1;i<=m;i++)s[i]=tmp[i];
		      }
			t*=k;
		}
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++)
	        if(i!=j)d[i][j]=1e9;
	        else d[i][j]=0;
	for(int i=1;i<=n-1;i++)scanf("%d%d",&u,&v),d[u][v]=d[v][u]=1,s[u].push_back(v),s[v].push_back(u);
	for(int i=1;i<=m;i++)scanf("%d",&a[i]),c[a[i]]=1;
	for(int k=1;k<=n;k++)
	    for(int i=1;i<=n;i++)
	        for(int j=1;j<=n;j++)
	            d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
	dfs1(1,0),cnt=0;
	for(int i=1;i<=m;i++)
	    if(!f[a[i]])a[++cnt]=a[i];
	m=cnt;
	for(int i=1;i<=n;i++)c[i]=0;
	for(int i=1;i<=m;i++)c[a[i]]=1;
	while((double)clock()/CLOCKS_PER_SEC<3.5)sa();
	printf("%d\n",ans);
	return 0;
}
posted @   w9095  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示