Codeforces 911F 贪心


分析:先求出树的直径,以这个直径上的节点作为主干。

假设两个端点分别为x,y,求其他任意叶子节点z到另一个叶子节点的最长距离即为max{ d(x,z), d(y,z) }

该解法的正确性我无法给出证明,但画图看看应该就不难理解的。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

typedef long long LL;
const int maxn = 2e5+10;
struct answer{
   int u,v;
}a[maxn];
vector<int> G[maxn];
bool v[maxn];
int d[maxn],pre[maxn];
int n,tot;
int root,leaf;
LL ans;

void init(){
   for (int i=1; i<=n; i++) G[i].clear();
   tot = 0;
   int u,v;
   for (int i=1; i<n; i++) {
      scanf("%d %d",&u,&v);
      G[u].push_back(v);
      G[v].push_back(u);
   }
   ans = 0;
}

void dfs(int u, int fa){
    d[u] = d[fa]+1;
    pre[u] = fa;
    for (int i=0; i<G[u].size(); i++) if (G[u][i]!=fa) dfs(G[u][i],u);
}

void Find(int x, int df, int dr, bool flag = 1){
    for (int i=0; i<G[x].size(); i++) {
        if (G[x][i] == pre[x] || v[G[x][i]]) continue;
        Find(G[x][i],df+1,dr+1);
    }
    if (flag) {
        if (df>dr) {
            a[tot].u = leaf;
            a[tot].v = x;
            ans += df;
        }
        else {
            a[tot].u = root;
            a[tot].v = x;
            ans += dr;
        }
        tot++;
    }
}

int main(){
    while (scanf("%d",&n)==1){
        init();

        d[0] = 0;
        root = 1;
        dfs(root,0);
        for (int i=1; i<=n; i++) if (d[root]<d[i]) root = i;
        leaf = 1;
        dfs(root,0);
        for (int i=1; i<=n; i++) if (d[leaf]<d[i]) leaf = i;
        memset(v,0,sizeof(v));
        int x = leaf;
        while (1){
            v[x] = 1;
            Find(x,d[leaf]-d[x],d[x]-1,0);
            if (x == root) break;
            x = pre[x];
        }

        x = leaf;
        while (x!=root) {
            a[tot].u = root; a[tot].v = x; tot++;
            ans += d[x]-1;
            x = pre[x];
        }

        printf("%I64d\n",ans);
        for (int i=0; i<tot; i++) printf("%d %d %d\n",a[i].u,a[i].v,a[i].v);
    }
    return 0;
}


Codeforces 2123F 问题中,目标是通过重新排列数组 $ a $ 来最小化不动点的数量。所谓“不动点”是指在重新排列后的数组中满足 $ a_i = i $ 的位置。该问题要求设计一种策略,以最优方式重新排列数组元素,使得这样的不动点数量最少。 为了解决这个问题,可以采用贪心算法和图论思想相结合的策略: - 首先,观察到如果某个值 $ i $ 出现了多次(即 $ a_i = i $),那么这些重复的值必须被移动到其他位置,以消除不动点。 - 对于那些没有出现在其索引上的值(例如 $ a_i \neq i $),可以通过交换操作将其移动到合适的位置,从而避免产生新的不动点。 一个有效的解决方案可以基于以下步骤: 1. 构建一个映射表,记录每个值出现的位置。 2. 找出所有当前值等于其索引的位置(即当前的不动点)。 3. 尝试通过交换来消除这些不动点。优先考虑将这些值移动到未被占用的位置,并确保不会引入新的不动点。 4. 在无法完全消除所有不动点的情况下,选择最优的交换策略以尽可能减少不动点的数量。 以下是 Python 中的一个示例实现,用于解决此类问题的基本思路: ```python def minimize_fixed_points(n, a): pos = {} fixed_points = [] # 记录每个值的出现位置,并找出初始的不动点 for i in range(n): if a[i] == i + 1: fixed_points.append(i) if a[i] not in pos: pos[a[i]] = [] pos[a[i]].append(i) # 如果没有重复的值,则可以直接交换以消除所有不动点 result = a[:] for i in fixed_points: found = False for val in pos: if val != i + 1 and len(pos[val]) > 0: j = pos[val].pop() result[i], result[j] = result[j], result[i] found = True break if not found: # 特殊情况处理:当只剩下一个值时 for j in range(n): if j != i and result[j] != j + 1: result[i], result[j] = result[j], result[i] break return result # 示例输入 n = int(input()) a = list(map(int, input().split())) result = minimize_fixed_points(n, a) print(' '.join(map(str, result))) ``` 此代码实现了上述逻辑,并尝试通过交换来最小化不动点的数量。对于大多数情况,它能够有效消除所有不动点;在某些特殊情况下(例如所有值都唯一且存在多个不动点),则需要特别处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值