洛谷 P3959 [NOIP2017 提高组] 宝藏

本文介绍了如何通过贪心、DFS和DP方法解决洛谷P3959中的一个问题,还提及了状态压缩优化技术。
摘要由CSDN通过智能技术生成

洛谷 P3959 [NOIP2017 提高组] 宝藏

题目链接洛谷 P3959 [NOIP2017 提高组] 宝藏


题目大意

一道奇奇怪怪的类似生成树的题目。

半正解 (过不了 H a c k Hack Hack

d f s + dfs+ dfs+贪心优化。( W A WA WA 了两个点,因为贪心正确性有点…)

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int BIGNUM=1e17;
const int MAXN=1000;
const int MAXM=4000;
const int MAXF=50100;
int hd[MAXM],nxt[MAXM],to[MAXM],l[MAXM],bz[MAXN],vis[MAXN],dep[MAXN],fnn[MAXN],id[MAXN],s[MAXF];
int n,m,cnt,top,ans=BIGNUM,maxn;

inline int mymin(int a,int b)
{
	return a<b?a:b;
}
void add(int u,int v,int vl)
{
	nxt[++cnt]=hd[u];
	hd[u]=cnt;
	to[cnt]=v;
	l[cnt]=vl;
	nxt[++cnt]=hd[v];
	hd[v]=cnt;
	to[cnt]=u;
	l[cnt]=vl;
}
void qsort(int l,int r)
{
	int i=l,j=r,mid=fnn[((l+r)>>1)+1];
	while(i<=j)
	{
		while(fnn[i]>mid)i++;
		while(fnn[j]<mid)j--;
		if(i<=j)
		{
			int op=fnn[i];
			fnn[i]=fnn[j];
			fnn[j]=op;
			op=id[i];
			id[i]=id[j];
			id[j]=op;
			i++,j--;
		}
	}
	if(l<j)qsort(l,j);
	if(i<r)qsort(i,r);
}

void dfs(int p,int sum,int fnn)
{
//	cout<<p<<endl;
	if(sum>ans) return ;
	if(p==n)
	{
		ans=mymin(ans,sum);
		return ;
	}
	for(int i=1;i<=top;i++)
	{
		if(sum>ans) return ;
		for(int j=hd[vis[i]];j;j=nxt[j])
		{
//			if(sum>ans) return ;
			if(sum+(dep[vis[i]]+1)*l[j]>ans) continue ;
		//	printf("%lld %lld\n",fnn[to[j]],ss*l[j]);
			if(bz[to[j]]==0&&s[fnn+(1<<to[j])]>sum+(dep[vis[i]]+1)*l[j])
			{
				bz[to[j]]=1;
				vis[++top]=to[j];
				dep[to[j]]=dep[vis[i]]+1;
				s[fnn+(1<<to[j])]=sum+dep[to[j]]*l[j];
			//	fnn[to[j]][dep[vis[i]+1]]=(dep[vis[i]]+1)*l[j];
				dfs(p+1,sum+dep[to[j]]*l[j],fnn+(1<<to[j]));
				vis[top]=0;
				top--;
				dep[to[j]]=0;
				bz[to[j]]=0;
			}
	//		else maxn=min(maxn,s[fnn+(1<<to[j])]);
		}
	}
}

signed main()
{
//	freopen("treasure.in","r",stdin);
//	freopen("treasure.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) id[i]=i;
	for(int i=1;i<=m;i++)
	{
		int u,v,vl;
		scanf("%lld%lld%lld",&u,&v,&vl);
		if(u!=v)
		{
			add(u,v,vl);
		}
	}
	qsort(1,n);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=50000;j++) s[j]=BIGNUM;
		top=0;
	//	s[(1<<id[i])]=0;
		vis[++top]=id[i];
		bz[id[i]]=1;
		dfs(1,0,(1<<id[i]));
		bz[id[i]]=0;
		
	}
//	cout<<maxn<<endl;
	printf("%lld",ans);
	return 0;
}

正解

神犇都用 D P DP DP ,我用 d f s + dfs+ dfs+状压优化。
当选了哪几个点时,将其压缩,放入数组,然后跑 d f s dfs dfs

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int INF=1e16;
const int MAXN=1100;
int vis[MAXN],l[MAXN],d[MAXN];
int c[MAXN][MAXN],to[MAXN][MAXN];
int ans=INF,tmp,tot,cnt,n,m,sum;

void qsort(int l,int r,int p)
{
	int i=l,j=r,mid=c[p][to[p][(l+r)/2+1]];
	while(i<=j)
	{
		while(c[p][to[p][i]]<mid)i++;
		while(c[p][to[p][j]]>mid)j--;
		if(i<=j)
		{
			swap(to[p][i],to[p][j]);
		//	swap(c[p][to[p][i]],c[p][to[p][j]]);
			i++,j--;
		}
	}
	if(l<j)qsort(l,j,p);
	if(i<r)qsort(i,r,p);
}

void dfs(int num,int node)
{
    for(int i=num;i<=sum;i++) 
	{
        if(tot+cnt*l[vis[i]]>=ans) return;
        for(int j=node;j<=d[vis[i]];j++)
        if(!l[to[vis[i]][j]])
		{ 
            sum++; 
            vis[sum]=to[vis[i]][j]; 
            cnt-=c[vis[sum]][to[vis[sum]][1]]; 
            tot+=c[vis[i]][vis[sum]]*l[vis[i]]; 
            l[vis[sum]]=l[vis[i]]+1; 
            dfs(i,j+1);
            tot-=c[vis[i]][vis[sum]]*l[vis[i]];
            l[vis[sum]]=0;
            cnt+=c[vis[sum]][to[vis[sum]][1]];
            sum--;
        }
        node=1;
    }
    if(sum==n)
	{
        if(tot<ans) ans=tot;
        return;
    }
}

signed main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
    {
    	for(int j=1;j<=n;j++)
    	{
    		c[i][j]=INF;
		}
	}
    for(int i=1;i<=m;i++)
	{
		int u,v,vl;
        scanf("%lld%lld%lld",&u,&v,&vl);
        if(c[u][v]<vl) continue ;
        if(c[u][v]==INF) to[u][++d[u]]=v,to[v][++d[v]]=u;
        c[u][v]=c[v][u]=vl;
    }
    for(int i=1;i<=n;i++)
	{
		qsort(1,d[i],i);
        cnt+=c[i][to[i][1]];
    }
    for(int i=1;i<=n;i++)
	{
        tot=0,sum=1;
        vis[1]=i;
        cnt-=c[i][to[i][1]];
        l[i]=1;
        dfs(1, 1);
        l[i]=0;
        cnt+=c[i][to[i][1]];
    }
    printf("%lld",ans);
    return 0;
}

简单易懂!!!

  • 45
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值