显然的,如果一个点的度大于,无解。
证明:
设一个点的度为,连接的三个点分别为,,
则:,,两两异色,,,两两异色,但是只有种颜色,并不存在一种方案满足条件(若满足前者,则满足不了与,,都异色)。
所以整个树就是一条链。
然后就能线性递推了。只要枚举前两个点的颜色就行了。
时间复杂度:
话说赛场上这种水题我居然没时间打了
清晰短小的代码:
xxxxxxxxxx#include <bits/stdc++.h>using namespace std;#define LL long long#define N 111111#define INF 1e15LL ind[N],ansp[N],g[N],f[N],nxt[N],d[N][3],lp[N],rp[N],ans=INF,n;//ind[i]:第i个点的度//ansp[]:方案//g[x]:第x个点的颜色//f[p]:链上第p个点的颜色//nxt[i]:链上第i个点的下一个节点//lp[i],rp[i]:第i个点的两个出边指向的点//ans:答案void search(int x,LL cost,int p){ //这是第x个点,当前花费为cost,这也是链上的第p个点if (nxt[x]==0){ //如果达到了链的另一个断点,则结束for (int i=0;i<3;++i){if (i!=f[p-1] && i!=f[p-2]){ //可以被使用的颜色。这里一定只有一个i满足要求f[p]=i;g[x]=i; //保存结果if (cost+d[x][i]<ans){ //如果方案更优ans=cost+d[x][i]; //保存结果和方案for (int j=1;j<=n;++j){ansp[j]=g[j]+1; //由于保存的时候颜色按照0,1,2保存,所以应该加一}}}}return;}if (p==1){ //如果这是链上的第一个点(链顶)for (int i=0;i<3;++i){ //分别枚举3种颜色f[p]=i;g[x]=i;search(nxt[x],cost+d[x][i],p+1);}}else if (p==2){ //如果这是链上的第二个点for (int i=0;i<3;++i){ //分别枚举可用的2种颜色,其中一个将被if语句过滤if (i!=f[1]){f[p]=i;g[x]=i;search(nxt[x],cost+d[x][i],p+1);}}}else{ //如果是其他的点for (int i=0;i<3;++i){if (i!=f[p-1] && i!=f[p-2]){ //这里一定只有一个i满足要求f[p]=i;g[x]=i;search(nxt[x],cost+d[x][i],p+1);}}}}int main(){ios::sync_with_stdio(false); //读入优化cin>>n;for (int i=1;i<=n;++i) cin>>d[i][0];for (int i=1;i<=n;++i) cin>>d[i][1];for (int i=1;i<=n;++i) cin>>d[i][2];for (int i=1;i<n;++i){int x,y;cin>>x>>y;ind[x]++;ind[y]++; //度if (ind[x]>=3 || ind[y]>=3){ //这不是链,输出-1puts("-1");return 0;}if (lp[x]==0) lp[x]=y; else rp[x]=y; //由于度只有2,所以无需使用链向星,只要将两个出边分别指向对应的点即可if (lp[y]==0) lp[y]=x; else rp[y]=x;}int root; //链的顶端for (int i=1;i<=n;++i){ //寻找链顶if (ind[i]==1){root=i;break;}}int now=lp[root],pre=root;nxt[root]=now; //nxt数组记录每个节点后面的节点while (true){ //将lp和rp的信息转化为nxt的信息if (ind[now]==1) break;if (lp[now]!=pre) nxt[now]=lp[now]; else nxt[now]=rp[now]; //判定下一个节点是哪个指针指向的pre=now;now=nxt[now]; //继续转化}search(root,0,1); //运行cout<<ans<<endl; //输出答案for (int i=1;i<n;++i){ //输出方案cout<<ansp[i]<<" ";}cout<<ansp[n]<<endl;return 0;}