【清澄竞技4.7】painting

好久没更新了,还是要总结一下

第一次参加集训队考试,拿了非集训队的第一,happy ing

主要是靠ac此题拿的分

题目大意:给出一颗n个节点的树,要给每一条边染一个1~n-1的颜色,染颜色i的代价为i,要求同一个节点连出的所有边所染颜色都互不相同,求一个为整棵树染色的方案,使得代价之和尽量小。

贪心明显是有后效性的,考虑设计f[i][j]表示i这棵子树,i到根选择j这种颜色,此子树的最优值,明显f[i][j]=min sigma(f[son][k]),其中k互不相等,且不等于j,观察转移,我们实际是为每个son这一个颜色且颜色不相同,使sigma最小,这就相当于是一个最优匹配的过程,(可以联想到ltc的部分搜索+匹配以及mt的noip模拟的一道树形dp题也是可以用匹配转移),由于每次我们只需对儿子进行两次找增广轨(因为颜色可能与父亲相同),所以均摊是n^3,考场上大部分人可能写的是状压(随机情况下期望较好),所以速度都比较慢,我的程序跑的应该是最快的吧


#include <cstdio>
#include <cstdlib>
#include <cstring>
const int oo=1073741819;
int lx[151],ly[151],_lx[151],_ly[151],slack[151],b[151][151],f[151][151],g[151],_g[151],rt[151],w[151],ry[151],n,d;
int pow[151][151],ans[151][151][151];
bool vx[151],vy[151],v[151];
int min(int x,int y) {return (x<y) ? x : y;}
int max(int x,int y) {return (x>y) ? x : y;}
int km(int x)
{
  int i,ne,tmp;
  if (vx[x]) return 0;
  vx[x]=1;
  for (i=1;i<=n-1;i++) {
    ne=i;
    if (v[ne]) continue;
    tmp=lx[x]+ly[ne]-f[x][ne];
    if (!vy[ne] && !tmp) {
      vy[ne]=1;
      if ( (!g[ne]) || (km(g[ne])) ) {
	g[ne]=x;return 1;
      }
    }
    else slack[ne]=min(slack[ne],tmp);
  }
  return 0;
}
int mysoul(int x)
{
  int sum=0,i,j,ne,na;
  for (i=1;i<=b[x][0];i++) {
    ne=b[x][i];
    if (ne==rt[x]) continue;
    memset(slack,127,sizeof(slack));memset(vx,0,sizeof(vx));memset(vy,0,sizeof(vy));
    for (;!km(ne);) {
      d=oo;
      for (j=1;j<=n-1;j++) if (!vy[j]) d=min(d,slack[j]);
      for (j=1;j<=b[x][j];j++) {
	na=b[x][j];
	if (vx[na]) lx[na]-=d;
      }
      for (j=1;j<=n-1;j++) if (vy[j]) ly[j]+=d;else slack[j]-=d;
      memset(vx,0,sizeof(vx));memset(vy,0,sizeof(vy));      
    }
  }
  for (i=1;i<=n-1;i++) 
    if (g[i]) sum+=lx[g[i]]+ly[i];
  return sum;
}
void origin(int x)
{
  int i,ne,j;
  for (i=1;i<=n-1;i++) ly[i]=0,g[i]=0;
  for (i=1;i<=b[x][0];i++) {
    ne=b[x][i];lx[ne]=-oo;
    for (j=1;j<=n-1;j++) lx[ne]=max(lx[ne],f[ne][j]);
  }
}
void cover(int x)
{
  int i,ne;
  for (i=1;i<=n-1;i++) _ly[i]=ly[i],_g[i]=g[i];
  for (i=1;i<=b[x][0];i++) {ne=b[x][i];_lx[ne]=lx[ne];}
}
void recover(int x)
{
  int i,ne;
  for (i=1;i<=n-1;i++) ly[i]=_ly[i],g[i]=_g[i];
  for (i=1;i<=b[x][0];i++) {ne=b[x][i];lx[ne]=_lx[ne];}  
}
void getans(int x,int i)
{
  int j;
  for (j=1;j<=n-1;j++)
    if (g[j]) ans[x][i][g[j]]=j;
}
void dfs(int x)
{
  int i,ne,tot,j;
  for (i=1;i<=b[x][0];i++) {
    ne=b[x][i];
    if (ne!=rt[x]) rt[ne]=x,ry[ne]=pow[x][i],dfs(ne);
  }
  origin(x);
  tot=mysoul(x);
  cover(x);
  for (i=1;i<=n-1;i++) {
    if (g[i]==0) {f[x][i]=tot-i,getans(x,i);continue ;}
    v[i]=1;
    memset(slack,127,sizeof(slack));memset(vx,0,sizeof(vx));memset(vy,0,sizeof(vy));
    for (;!km(g[i]);) {
      d=oo;
      for (j=1;j<=n-1;j++) if (!vy[j]) d=min(d,slack[j]);
      for (j=1;j<=b[x][j];j++) {
	ne=b[x][j];
	if (vx[ne]) lx[ne]-=d;
      }
      for (j=1;j<=n-1;j++) if (vy[j]) ly[j]+=d;else slack[j]-=d;
      memset(vx,0,sizeof(vx));memset(vy,0,sizeof(vy));
    }
    d=0;
    for (j=1;j<=n-1;j++) 
      if (g[j] && !v[j]) {
	d+=lx[g[j]]+ly[j];
	ans[x][i][g[j]]=j;
      }
    f[x][i]=d-i;v[i]=0;
    recover(x);
  }
}
void link(int x,int y,int i) 
{
  b[x][++b[x][0]]=y,b[y][++b[y][0]]=x;
  pow[x][b[x][0]]=i,pow[y][b[y][0]]=i;
}
void getout(int x,int y)
{
  int i,ne;
  for (i=1;i<=b[x][0];i++) {
    ne=b[x][i];
    if (ne!=rt[x]) {
      w[ry[ne]]=ans[x][y][ne];
      getout(ne,ans[x][y][ne]);
    }
  }
}
void init()
{
  int i,x,y,tot;
  scanf("%d\n",&n);
  for (i=1;i<=n-1;i++) {
    scanf("%d%d\n",&x,&y);
    link(x,y,i);
  }
  for (i=1;i<=b[1][0];i++) 
    rt[b[1][i]]=1,ry[b[1][i]]=pow[1][i],dfs(b[1][i]);
  origin(1);
  tot=mysoul(1);
  printf("%d\n",-tot);
  for (i=1;i<=n-1;i++)
    if (g[i]) w[ry[g[i]]]=i,getout(g[i],i);
  for (i=1;i<=n-1;i++) printf("%d ",w[i]);
}
int main()
{
  freopen("painting.in","r",stdin);
  freopen("2painting.out","w",stdout);
   init();
  return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值