Sol
题目大意
有一棵树,你需要将N个点染成M种颜色,且需满足一下两种条件
1.一号点必须是一号颜色,且一号颜色必须包含K个点
2.每种颜色必须包含至少一个点
代价为若一条边连接的颜色相同则得付出该边的代价
求满足以上两种情况下的代价之和
题解
首先,一眼看出是道树dp题
然后日常套路,设
f[u][j]
f
[
u
]
[
j
]
表示以
u
u
为子树包含种一号颜色下的最小代价,然后发现无法判断两个点之间是否颜色相同,于是再加一维
f[u][j][0/1]
f
[
u
]
[
j
]
[
0
/
1
]
表示在
u
u
<script type="math/tex" id="MathJax-Element-480">u</script>号点上是否染为一号颜色
然后开始最重要的yy
如果M==2
的话,一条边连接的两个点只有一个为1一个不为1的情况下才不会付出代价
如果M>2
的话,一条边连接的两个点只要不是都为1就不会付出代价
为什么呢?
显然可以通过奇偶性来强制两个点颜色不同,且满足条件
然后dp方程就是
f[u][j][0]=min(f[u][j][0],min(f[v][t][0]+f[u][j-t][0]+(M==2)*e[i].w,f[v][t][1]+f[u][j-t][0]));
f[u][j][1]=min(f[u][j][1],min(f[v][t][1]+f[u][j-t][1]+e[i].w,f[v][t][0]+f[u][j-t][1]));
然后惊奇的发现其实我们连样例都过不了(尴尬)
于是重新审视dp方程
我们需要每次dp前将之前的f数组备份下来,不然的话f值在dp中会越来越小,当然如果N也很小的话其实也没什么影响
所以我们用tmp[j][0/1]来存f[u]数组的值
f[u][j][0]=min(f[u][j][0],min(f[v][t][0]+tmp[j-t][0]+(M==2)*e[i].w,f[v][t][1]+tmp[j-t][0]));
f[u][j][1]=min(f[u][j][1],min(f[v][t][1]+tmp[j-t][1]+e[i].w,f[v][t][0]+tmp[j-t][1]));
然后就能愉快的AC了
好了,上代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int _=305;
inline int read()
{
char ch='!';int z=1,num=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')z=-1,ch=getchar();
while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
return z*num;
}
int N,M,K,f[_][_][2],tmp[_][2];
struct hand{int to,next,w;}e[_<<1];
int cnt,head[_];
void link(int u,int v,int w)
{
e[++cnt]=(hand){v,head[u],w};
head[u]=cnt;
}
void dfs(int u,int fa)
{
f[u][0][0]=f[u][1][1]=0;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
memcpy(tmp,f[u],sizeof(tmp));
memset(f[u],63,sizeof(f[u]));
for(int j=0;j<=K;++j)
{
for(int t=0;t<=j;++t)
{
f[u][j][0]=min(f[u][j][0],min(f[v][t][0]+tmp[j-t][0]+(M==2)*e[i].w,f[v][t][1]+tmp[j-t][0]));
f[u][j][1]=min(f[u][j][1],min(f[v][t][1]+tmp[j-t][1]+e[i].w,f[v][t][0]+tmp[j-t][1]));
}
}
}
}
int main()
{
N=read();M=read();K=read();
if(N-K<M-1){puts("-1");return 0;}
for(int i=1;i<N;++i)
{
int x=read(),y=read(),z=read();
link(x,y,z);link(y,x,z);
}
memset(f,63,sizeof(f));
dfs(1,1);
printf("%d\n",f[1][K][1]);
return 0;
}