题目链接:https://vjudge.net/contest/335474#problem/D
翻译:
给定n个顶点,三种颜色,每个顶点涂对应颜色的花费。
接下来是n-1条边,保证给定的边能构成一棵树。
求涂色的最小花费。
涂色要求:相连的三个顶点不能涂一样的颜色。
分析:
因为相邻的三个顶点不能涂一样的颜色,由此可知,当一个点的度数>=3时,不符合情况,直接输出-1。
所以一棵树就退化为一条链。
读入边的时候用邻接表存图。
从度数为1的点开始遍历,找到一条路径,再遍历所有的情况,找到最小值即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2*1e5+10;
const long long inf=0x3f3f3f3f3f3f3f3f;
typedef long long LL;
int u[N],v[N],first[N],net[N];
int c[3][N];
int degree[N],path[N];
bool book[N];
int n,k;
int x;
int xx,yy,zz;
LL sum,mi;
void serve(int a,int b)
{
u[k]=a,v[k]=b;
net[k]=first[a];
first[a]=k;
k++;
}
void dfs(int tail)
{
path[x++]=tail;
book[tail]=true;
for(int i=first[tail]; i!=-1; i=net[i])
{
if(book[v[i]])
continue;
dfs(v[i]);
}
}
void solve(int a,int b,int cc)
{
a--,b--,cc--;
sum=0;
for(int i=1; i<x; i++)
{
int t=path[i];
if(i%3==1)
sum+=(LL)c[a][t];
if(i%3==2)
sum+=(LL)c[b][t];
if(i%3==0)
sum+=(LL)c[cc][t];
}
if(sum<mi)
{
mi=sum;
xx=a;
yy=b;
zz=cc;
}
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0; i<3; i++)
for(int j=1; j<=n; j++)
scanf("%d",&c[i][j]);
k=1;
x=1;
memset(degree,0,sizeof(degree));
memset(first,-1,sizeof(first));
memset(book,false,sizeof(book));
for(int i=1; i<n; i++)
{
int a,b;
scanf("%d%d",&a,&b);
serve(a,b);
serve(b,a);/*邻接表存图*/
degree[a]++;
degree[b]++;
}
int flag=0,head;
for(int i=1; i<=n; i++)
{
if(degree[i]>=3)
flag=1;
if(degree[i]==1)
head=i;/*开头为那个点*/
}
if(flag)
printf("-1\n");
else
{
dfs(head);
mi=inf;
solve(1,2,3);/*遍历所有的情况*/
solve(1,3,2);
solve(2,1,3);
solve(2,3,1);
solve(3,1,2);
solve(3,2,1);
printf("%lld\n",mi);
int v[N];
for(int i=1; i<x; i++)
{
int t=path[i];
if(i%3==1)
v[t]=xx;
if(i%3==2)
v[t]=yy;
if(i%3==0)
v[t]=zz;
}
for(int i=1; i<x; i++)
printf("%d ",v[i]+1);
printf("\n");
}
}
return 0;
}
衣带渐宽终不悔,为伊消得人憔悴。