2023NOIP A层联测14 修路

文章介绍了一个关于图论的问题,通过动态规划求解连通图的生成树,最小化中心点周围点的拥挤指数,同时考虑多种方案。

题目大意

有一个有nnn个点mmm条边的无向连通图,第iii条边连接点uiu_iuiviv_ivi,长度为lil_ili

你想要求这个图的一棵生成树,并规定一个中心点midmidmid。定义一种规划的拥挤指数为k×S+∑i=1ndis(i,mid)k\times S+\sum\limits_{i=1}^ndis(i,mid)k×S+i=1ndis(i,mid),其中有kkk表示只被一条边连接的点的数量,dis(u,v)dis(u,v)dis(u,v)表示点uuu到点vvv的距离,SSS为给定的常数。

求如何修建道路和规划中心点,才能使拥挤程度最小,以及有多少种方案(求生成树+规划中心点)能使拥挤程度最小。

1≤n≤15,n−1≤m≤n(n−1)2,0≤li,S≤1091\leq n\leq 15,n-1\leq m\leq \frac{n(n-1)}{2},0\leq l_i,S\leq 10^91n15,n1m2n(n1),0li,S109

时间限制7000ms7000ms7000ms,空间限制1024MB1024MB1024MB


题解

我们枚举每个点,让它作为中心点,并找到一个拥挤程度最小的生成树。

我们发现∑i=1ndis(i,mid)\sum\limits_{i=1}^ndis(i,mid)i=1ndis(i,mid)其实等于∑i=1ntfi×sizi\sum\limits_{i=1}^ntf_i\times siz_ii=1ntfi×sizi,其中lenilen_ileni表示当midmidmid为根时点iii到父亲的距离,sizisiz_isizi表示子树iii的大小。

因为nnn比较小,我们可以使用状压DPDPDP

f[i][s]f[i][s]f[i][s]表示当前的根节点为iiiiii的子树中点的集合为sss时的拥挤程度和方案数(要保存两个数,可以用结构体),g[i][s]g[i][s]g[i][s]表示当前根节点为iii且根节点只有一个儿子,iii的子树中(不包括iii)的点的集合为sss时的拥挤程度和方案数(其实就是一个连通块连出一条边到点iii)。注意虽然根节点有可能只被一条边连接,但当sss不为点的全集时,这里的iii都不算入kkk中。

初始值为f[i][2i−1]={0,1}f[i][2^{i-1}]=\{0,1\}f[i][2i1]={0,1},其余的fff值和ggg值为{+∞,0}\{+\infty,0\}{+,0}。其中第一个数为拥挤程度,第二个数为方案数。

枚举当前状态sss,当前点uuusss的子集ttt,现在ttt表示要新加入的子树的状态,则转移式为

f[u][s]=max⁡(f[u][s],(f[u][s⊕t]∗g[u][t])+v)f[u][s]=\max(f[u][s],(f[u][s\oplus t]*g[u][t])+v)f[u][s]=max(f[u][s],(f[u][st]g[u][t])+v)

其中max⁡\maxmax表示两个状态取拥挤程度最小的状态,如果拥挤程度相同则将方案数求和。∗*表示两个状态合并,即拥挤程度相加、方案数相乘。当sss为点的全集且uuu只被一条边连接时v=Sv=Sv=S,否则v=0v=0v=0,这里的vvv是加在拥挤程度上的。

下面,枚举与uuu相连且不在sss中的点vvv(这是为能在之后将vvv加入这个连通块),则转移式为

g[v][s]=max⁡(g[v][s],f[u][s]+l∗cts+[cts==1]∗S)g[v][s]=\max(g[v][s],f[u][s]+l*ct_s+[ct_s==1]*S)g[v][s]=max(g[v][s],f[u][s]+lcts+[cts==1]S)

其中max⁡\maxmax+++的意义同上,lll表示这条边的长度,ctsct_scts表示sss的二进制位中111的个数(其实就是在算上面的式子∑i=1ntfi×sizi\sum\limits_{i=1}^ntf_i\times siz_ii=1ntfi×sizi),后面[cts==1]∗S[ct_s==1]*S[cts==1]S表示如果vvv下面只有一个点,也就是有一个只被一条边连接的点,那么拥挤程度要加上SSS

最后,用每个f[i][2n−1]f[i][2^n-1]f[i][2n1]来更新ansansansansansans也是一个结构体,初始值为{+∞,0}\{+\infty,0\}{+,0}),则ansansans中保存的就是最小的拥挤程度以及能使拥挤程度最小的方案数。

时间复杂度为O(3n⋅n2)O(3^n\cdot n^2)O(3nn2)

code

#include<bits/stdc++.h>
using namespace std;
const int N=15,M=15;
const long long inf=1e15;
int n,m,ct[1<<N];
long long S;
struct node{
	int y,w;
};
struct dp{
	long long vl,s;
	friend dp operator+(dp ax,long long bx){
		ax.vl+=bx;
		return ax;
	}
	friend dp operator*(dp ax,dp bx){
		ax.vl+=bx.vl;
		ax.s*=bx.s;
		return ax;
	}
}ans,f[N+5][1<<N],g[N+5][1<<N];
vector<node>G[N+5];
int lb(int i){return i&(-i);}
dp gtmx(dp ax,dp bx){
	if(ax.vl==bx.vl) ax.s+=bx.s;
	if(ax.vl<=bx.vl) return ax;
	return bx;
}
int main()
{
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);
	scanf("%d%d%lld",&n,&m,&S);
	for(int i=1,x,y,w;i<=m;i++){
		scanf("%d%d%d",&x,&y,&w);
		G[x].push_back((node){y,w});
		G[y].push_back((node){x,w});
	}
	for(int i=1;i<(1<<n);i++) ct[i]=ct[i^lb(i)]+1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<(1<<n);j++){
			f[i][j]=(dp){inf,0};g[i][j]=(dp){inf,0};
		}
		f[i][(1<<i-1)]=(dp){0,1};
	}
	for(int s=1;s<(1<<n);s++){
		for(int u=1;u<=n;u++){
			if(!((s>>u-1)&1)) continue;
			int vs=s^(1<<u-1);
			for(int t=vs;t;t=(t-1)&vs){
				if(t&lb(vs)){
					long long v=(s==(1<<n)-1&&t==vs)*S;
					f[u][s]=gtmx(f[u][s],(f[u][s^t]*g[u][t])+v);
				}
			}
			for(int i=0;i<G[u].size();i++){
				int v=G[u][i].y,w=G[u][i].w;
				if((s>>v-1)&1) continue;
				g[v][s]=gtmx(g[v][s],f[u][s]+w*ct[s]+(ct[s]==1)*S);
			}
		}
	}
	ans=(dp){inf,0};
	for(int i=1;i<=n;i++){
		ans=gtmx(ans,f[i][(1<<n)-1]);
	}
	printf("%lld %lld\n",ans.vl,ans.s);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值