Colourful Graph 2016香港

本文详细解析了Kattis平台上的Colourful问题,介绍了如何通过调整树中节点的颜色来匹配目标树的颜色配置,使用先序遍历优化节点移动,确保子树稳定,最终实现不超过20万次的交换操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://open.kattis.com/problems/colourful

实际上用一棵树的边就行了,如果原树每种目标树有点颜色都有,那么就一定能行

那么把一棵树抠出来,然后某一种颜色少了,就随便找到一个点,然后再随便找一些比目标数多的颜色的点,一路交换到这个点旁边,然后把多的点变成这个少了的颜色

颜色数量一样后,再按照先序遍历的顺序,一个点一个点地安排,按先序遍历的目的是,已经搞好的子树就不用再移动他们了。

最多n个点颜色不一样,交换颜色数量最多n次,所以统一颜色数最多用10000次。然后一个点一个点地安排,每次最多交换n个点,那么也是最多用10000次,上限刚好200000次

代码格式有点难看,用emacs写的,交上去就成这样了。。

#include<bits/stdc++.h>
using namespace std;

const int maxl=110;

int n,m,k,flag,ans,ansv;
int a[maxl],ta[maxl],num[maxl],tnum[maxl];
int frm[maxl];
bool vis[maxl];
vector<int> e[maxl],ee[maxl],b;

inline void predfs(int u)
{
  vis[u]=true;
  for(int v:ee[u])
    if(!vis[v])
      {
	e[u].push_back(v);
	e[v].push_back(u);
	predfs(v);
      }
  b.push_back(u);
}


inline void prework()
{
  scanf("%d%d%d",&n,&m,&k);
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),num[a[i]]++;
  for(int i=1;i<=n;i++)
    scanf("%d",&ta[i]),tnum[ta[i]]++;
  int u,v;
  for(int i=1;i<=m;i++)
    {
      scanf("%d%d",&u,&v);
      ee[u].push_back(v);
      ee[v].push_back(u);
    }
  predfs(1);
}

inline void print()
{
  for(int i=1;i<=n;i++)
    printf("%d%c",a[i]," \n"[i==n]);
}

inline void dfs(int u)
{
  vis[u]=true;
  if(num[a[u]]>tnum[a[u]])
    {
      ansv=u;
      return;
    }
  for(int v:e[u])
    if(!vis[v])
      {
	frm[v]=u;
	dfs(v);
	if(ansv) return;
      }
}

inline void findcol(int u,int col)
{
  vis[u]=true;
  if(a[u]!=ta[u] && a[u]==col)
    {
      ansv=u;
      return;
    }
  for(int v:e[u])
    if(!vis[v])
      {
	frm[v]=u;
	findcol(v,col);
	if(ansv) return;
      }
}

inline void mainwork()
{
  ans=1;
  for(int i=0;i<k;i++)
    if(tnum[i]>0 && num[i]==0)
      ans=0;
  if(!ans) 
    {
      puts("Impossible");
      return; 
    }
  print();
  int u,v;
  for(int i=0;i<k;i++)
    if(num[i]<tnum[i])
      {
	for(int j=1;j<=n;j++)
	  if(a[j]==i)
	    {u=j;break;}
	while(num[i]<tnum[i])
	  {
	    for(int j=1;j<=n;j++)
	      frm[j]=0,vis[j]=false;
	    ansv=0;
	    dfs(u);v=ansv;
	    while(frm[v]!=u)
	      {
		swap(a[v],a[frm[v]]);
		v=frm[v];print();
	      }
	    num[a[v]]--;num[i]++;
	    a[v]=i;print();
	    
	  }
      }
  for(int d:b)
    if(a[d]!=ta[d])
      {
	for(int i=1;i<=n;i++)
	  frm[i]=0,vis[i]=false;
	ansv=0;
	findcol(d,ta[d]);v=ansv;
	while(v!=d)
	  {
	    swap(a[v],a[frm[v]]);
	    v=frm[v];print();
	  }
      }
  
}

int main()
{
  prework();
  mainwork();
  return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值