jzoj5442. 【NOIP2017提高A组冲刺11.1】荒诞

2 篇文章 0 订阅
1 篇文章 0 订阅

jzoj5442

Description

一个无向图,最长的简单路径长度不超过10,要求每个点要么被选,要么有一个直接相连的点被选,求最小代价

Solution
30pts

暴力

45pts

加上树形DP

满分做法

还是考虑DP,由于原图是无向图,其 dfs 树深度不超过10,因此考虑跟树形DP一样做

f x , s f_{x,s} fx,s 表示已经覆盖完 欧拉序/dfs序 在x之前的点(x到根路径上的点除外),x到根路径上的点状态为s,最小代价

往下传时枚举祖先的状态,分选和不选两种情况转移

往上传时比较简单

时间复杂度 O ( n ∗ 3 10 ) O(n*3^{10}) O(n310),然而状态数明显跑不满,空间上根据深度滚动一下就可以

code
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
const int INF=1e9+7;
using namespace std;
const int N=2e4+10;
const int M=2.5e4+10;
const int K=59050;
int n,m,a[N],f[20][K],depth[N],cnt,q[N*2];
int tot,last[N],mi[20],dis,ans;
bool bz[N];
struct edge{
	int st,en,next;
}E[M*2];
void dfs(int x){
	q[++cnt]=x;
	bz[x]=1;
	for(int p=last[x];p;p=E[p].next){
		int y=E[p].en;
		if(bz[y])continue;
		depth[y]=depth[x]+1;
		dfs(y);
		q[++cnt]=x;
	}
}
void add(int x,int y){
	E[++tot]=(edge){x,y,last[x]};
	last[x]=tot;
}
int main(){
	freopen("absurdity.in","r",stdin);
	freopen("absurdity.out","w",stdout);
	mi[0]=1;
	fo(i,1,10)mi[i]=mi[i-1]*3;
	scanf("%d%d",&n,&m);
	fo(i,1,n)scanf("%d",&a[i]);
	fo(i,1,m){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	fo(i,1,n){
		if(bz[i])continue;
		cnt=0;
		dfs(i);
		memset(f[0],127,sizeof(f[0]));
		dis=f[0][0];
		f[0][0]=0;
		f[0][2]=a[i];
		fo(i,2,cnt){
			int x=depth[q[i]];
			memset(f[x],127,sizeof(f[x]));
			if(x>depth[q[i-1]]){
				fo(j,0,mi[x]-1){
					if(f[x-1][j]==dis)continue;
					int s=j;
					bool flag=0;
					for(int p=last[q[i]];p;p=E[p].next){
						int y=E[p].en;
						if(depth[y]>x)continue;
						if(j/mi[depth[y]]%3==2)flag=1;
						else s=(s/mi[depth[y]+1]*3+1)*mi[depth[y]]+s%mi[depth[y]];
					}
					if(j/mi[x-1]==2)flag=1;
					else s=s%mi[x-1]+mi[x-1];
					s+=mi[x]*2;
					if(flag)f[x][j+mi[x]]=min(f[x][j+mi[x]],f[x-1][j]);
					if(j/mi[x-1]!=2)f[x][j]=min(f[x][j],f[x-1][j]);
					f[x][s]=min(f[x][s],f[x-1][j]+a[q[i]]);
				}
			}else{
				fo(j,0,mi[x+1]-1){
					f[x][j]=min(f[x+1][j+mi[x+1]],f[x+1][j+2*mi[x+1]]);
				}
			}
		}
		ans+=min(f[0][1],f[0][2]);
	}
	printf("%d\n",ans);
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值