HDU 4735 Little Wish~ lyrical step~(DLX算法、弗洛伊德算法)

本文介绍了一种解决特定树形结构问题的方法,通过将问题转化为重复覆盖问题,并使用剪枝技术和弗洛伊德算法优化求解过程,旨在找到最小的操作次数使每个节点满足条件。

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

题目:Little Wish~ lyrical step~

题意:给定一棵树,每个结点有一个男孩或女孩。给出树的边的长度。唯一的操作是对任意两个孩子交换位置。最后要使得每个女孩在D距离内至少有一个男孩。求最少操作次数,不能达到的输出-1。

树的点数最多50个。

可以转化成重复覆盖问题来求。

首先统计出男生的人数cnt,那么问题转化成在N个点中选择至多cnt个点,使得所有点都被覆盖。

然后对于一个可行方案,看看哪些点原来是女生,说明要执行多少次交换。

加上两个剪枝。

第一个还是经典的手段,看看当前至少还需要多少个 男生,如果加起来超过cnt个,剪枝。

第二个是看看当前已经选的位置,上面已经有多少次交换,如果比当前的答案ans大,也可以剪掉。

点与点之间的距离就直接上弗洛伊德好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 1000000000;
const int N = 51;
const int M = 3000;
int T, n, dis, cnt, sz, ans;
int d[N][N], f[N];
int U[M], D[M], L[M], R[M], col[M], S[N], row[M], app[N], cur[N];
void init(){
    for(int i=0; i<=n; i++){
        L[i] = i-1;
        R[i] = i+1;
        U[i] = D[i] = i;
        S[i] = 0;
        app[i] = -1;
    }
    L[0] = n;
    R[n] = 0;
    sz = n+1;
}
void link(int r, int c){
    row[sz] = r;
    col[sz] = c;
    U[sz] = U[c];
    D[sz] = c;
    U[D[sz]] = D[U[sz]] = sz;
    S[c]++;
    if(~app[r]){
        L[sz] = app[r];
        R[sz] = R[app[r]];
        L[R[sz]] = R[L[sz]] = sz;
    }
    else{
        L[sz] = R[sz] = sz;
    }
    app[r] = sz;
    sz++;
}
bool vis[N];
bool h(int dep){
    memset(vis, 0, sizeof(vis));
    for(int i=R[0]; i; i=R[i]){
        if(!vis[i]){
            vis[i] = 1;
            dep++;
            if(dep>cnt) return 0;
            for(int j=D[i]; j!=i; j=D[j]){
                for(int k=R[j]; k!=j; k=R[k]){
                    vis[col[k]] = 1;
                }
            }
        }
    }
    return 1;
}
void remove(int x){
    for(int i=D[x]; i!=x; i=D[i]){
        L[R[i]] = L[i];
        R[L[i]] = R[i];
    }
}
void restore(int x){
    for(int i=U[x]; i!=x; i=U[i]){
        L[R[i]] = R[L[i]] = i;
    }
}
void dfs(int dep, int ex){
    if(ex>=ans || ex>cnt || !h(dep)) return;
    if(!R[0]){
        ans = ex;
        return;
    }
    int c = R[0];
    for(int i=R[c]; i; i=R[i]){
        if(S[i]<S[c])   c=i;
    }
    for(int i=D[c]; i!=c; i=D[i]){
        cur[dep] = row[i];
        remove(i);
        for(int j=R[i]; j!=i; j=R[j])   remove(j);
        dfs(dep+1, ex+(!f[row[i]]));
        for(int j=L[i]; j!=i; j=L[j])   restore(j);
        restore(i);
    }
}
int solve(){
    init();
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            if(d[i][j]<=dis)  link(i, j);
        }
    }
    ans = inf;
    dfs(0, 0);
    return ans>cnt?-1:ans;
}
int main(){
    scanf("%d", &T);
    for(int t=1; t<=T; t++){
        scanf("%d %d", &n, &dis);
        cnt = 0;
        for(int i=1; i<=n; i++){
            scanf("%d", f+i);
            cnt+=f[i];
            for(int j=1; j<=n; j++) d[i][j] = i==j?0:inf;
        }
        int a, b, c;
        for(int i=1; i<n; i++){
            scanf("%d %d %d", &a, &b, &c);
            d[a][b] = d[b][a] = c;
        }
        for(int k=1; k<=n; k++){
            for(int i=1; i<=n; i++){
                for(int j=1; j<=n; j++){
                    d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
                }
            }
        }
        printf("Case #%d: %d\n", t, solve());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值