UVa1667/LA3405 Network Mess

UVa1667/LA3405 Network Mess

题目链接

  本题是2005年icpc亚洲区域赛东京赛区的题目

题意

  有一棵n(n≤50)个叶子的无权树。输入两两叶子的距离,恢复出这棵树并输出每个非叶子结点的度数。

分析

  先处理1、2号叶结点,其距离为a[1][2],则有a[1][2]-1个初始非叶结点,且每个非叶结点i到1、2号叶结点的距离d[i][1]、d[i][2]都知道。再考虑加入3号叶节点多出来的那一个分支:先找出是从初始的a[1][2]-1个非叶结点哪一个结点开始延伸出来的,假设是结点x,则a[1][3]-a[2][3] == d[x][1]-d[x][2],后面需要再添加a[1][3]-d[x][1]-1个非叶结点,并且新添加的每个非叶结点k到1、2、3号叶结点的距离d[k][1]、d[k][2]、d[k][3]都容易得到,但还需要计算出初始的a[1][2]-1个非叶结点到3号叶结点,这个就不直观了,高效的计算方法稍后再说。
  一般地,加入第i(i>2)号结点后,首先找出是从已经存在的m个非叶结点的哪个结点开始延伸出来,可以通过最多m*(i-2)次的遍历判断找出(二重循环):外层循环枚举m个非叶结点x,内层循环枚举k(0<k<i),如果a[k-1][i]-a[k][i] != d[x][k-1]-d[x][k]则不是从结点x延伸出来的。找出是延伸结点x后,就知道需要再加a[1][i]-d[x][1]-1个非叶结点并且可得出每个新添加的每个非叶结点k到前i个叶结点的距离d[k][1]、d[k][2]、…、d[k][i],但还需要计算出每个旧非叶结点k到叶结点i的距离d[k][i],高效的计算方法: d [ k ] [ i ] = d [ x ] [ i ] + m a x { d [ k ] [ p ] − d [ x ] [ p ]    ∣    1 ≤ p < i } d[k][i]=d[x][i]+max\{d[k][p]-d[x][p]\;\mathbf{|}\;1≤p<i\} d[k][i]=d[x][i]+max{d[k][p]d[x][p]1p<i},枚举前i-1个叶结点即可得到。

AC 代码

#include <iostream>
#include <algorithm>
using namespace std;

#define M 701
#define N 50
int a[N][N], d[M][N], c[M], m, n;

bool check(int i, int j) {
    for (int k=1; k<i; ++k) if (a[i][k]-a[i][k-1] != d[j][k]-d[j][k-1]) return false;
    return true;
}

void solve() {
    for (int i=m=0; i<n; ++i) for (int j=0; j<n; ++j) cin >> a[i][j];
    for (int j=a[0][1]-1; j>0; --j) d[m][0] = m+1, d[m][1] = j, c[m++] = 2;
    for (int i=2; i<n; ++i) for (int j=0; j<m; ++j) if (check(i, j)) {
        ++c[j]; d[j][i] = a[i][0]-d[j][0];
        for (int k=0, dd; k<m; ++k) {
            for (int p=dd=0; p<i; ++p) dd = max(dd, d[k][p]-d[j][p]);
            d[k][i] = d[j][i]+dd;
        }
        for (int k=1; k<d[j][i]; ++k) {
            for (int p=0; p<i; ++p) d[m][p] = d[j][p]+k;
            d[m][i] = d[j][i]-k; c[m++] = 2;
        }
        break;
    }
    sort(c, c+m); cout << c[0];
    for (int i=1; i<m; ++i) cout << ' ' << c[i];
    cout << endl;
}

int main() {
    while (cin>>n && n) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值