H - Little Wish~ lyrical step~ HDU - 4735 跳舞链

86 篇文章 0 订阅
8 篇文章 0 订阅

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4735

 

题意:

       给你一棵无向有权树,每个结点上有一个男生或者女生,当一个女生开始的距离小于等于d的范围内有一个男生,那么这个女生就算是被保护着了。现在问,你要交换几个男生的位置可以让所有的女生被保护

做法:

       为了这道题特意去学了一下跳舞链,知道了跳舞链不仅仅能用来解决精确的单点问题,也可以进行一番修改后来进行重复的范围维护。

        把横坐标当成男生,列坐标当成女生,k就相当于是我选择的男生的数量,每次都要去做一下剪枝。如当前的选择男生数加上还能选多少不能超过总男生的数量,以及现在的答案要小于我之前的答案的时候在继续。


#include<bits/stdc++.h>

using namespace std;
const int maxn=70;
const int maxm=70;
const int maxnode=60*60+60;
const int head=0;
const int inf=(int)1e9+7;
typedef long long ll;
int L[maxnode],R[maxnode],U[maxnode],D[maxnode],Row[maxnode];
int C[maxnode],H[maxnode],coln[maxm],ans[maxn];
int n,d,sz,len,isboy[maxn],dis[maxn][maxn],fans,nboy;
bool yes;
void init_dis(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) dis[i][j]=0;
            else dis[i][j]=inf;
        }
    }
}
void init(){
    for(int i=0;i<=n;i++){
        coln[i]=0; U[i]=D[i]=i;
        L[i+1]=i;R[i]=i+1;
    }
    R[n]=0; L[0]=n;
    sz=n+1;
    int tmp=n;
    while(tmp) H[tmp]=-1,tmp--;
}
void addLink(int r,int c){
    coln[c]++; C[sz]=c; Row[sz]=r;
    U[sz]=U[c]; D[U[c]]=sz;
    D[sz]=c; U[c]=sz;
    if(H[r]==-1) H[r]=L[sz]=R[sz]=sz;
    else{
        L[sz]=L[H[r]];  R[L[sz]]=sz;
        R[sz]=H[r]; L[H[r]]=sz;
    }
    sz++;
}
//将该数的这一列的左右全部都变化
void Remove(int sz){
    for(int i=D[sz];i!=sz;i=D[i]){
        L[R[i]]=L[i];
        R[L[i]]=R[i];
        coln[C[i]]--;
    }
}
void Resume(int sz){
    for(int i=U[sz];i!=sz;i=U[i]){
        L[R[i]]=i;
        R[L[i]]=i;
        coln[C[i]]++;
    }
}
bool vis[maxn];
int Calmax(){
    int ret=0;

    memset(vis,false,sizeof(vis));
    for(int i=R[head];i!=head;i=R[i]){
        if(vis[i]==false){
            ret++;
            vis[i]=true;
            for(int j=D[i];j!=i;j=D[j]){
                for(int k=R[j];k!=j;k=R[k]){
                    vis[C[k]]=true;
                }
            }
        }
    }
    return ret;
}
void DLX(int k){
    //假设我选择了k个人要当做男生
    //但是在计算答案的时候 我需要统计其中有多少个是男生并减掉
    
    if(k+Calmax()>nboy) return ;
    //如果再多选一些集合当男生的时候数量超了就返回
    int seboy=0;
    for(int i=0;i<k;i++)
        seboy+=isboy[ans[i]];
    int nowans=k-seboy;

    if(nowans>=fans) return ;
    if(R[head]==head){
        fans=nowans;
        yes=true;
        return ;
    }
    int minn=maxn,minpos=-1;
    for(int i=R[0];i!=0;i=R[i]){
        if(minn>coln[i]){
            minn=coln[i];  minpos=i;
        }
    }
    
    for(int i=D[minpos];i!=minpos;i=D[i]){
        ans[k]=Row[i];
        Remove(i);
        for(int j=R[i];j!=i;j=R[j]) {
            Remove(j);
        }
        DLX(k+1);
        for(int j=L[i];j!=i;j=L[j]) Resume(j);
        Resume(i);
    }
}
int main(){
    int T,cas=0; cin>>T;
    while(T--){
        scanf("%d%d",&n,&d);
        init_dis();
        init();
        nboy=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&isboy[i]);
            nboy+=isboy[i];
        }
        for(int i=1;i<n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            dis[a][b]=dis[b][a]=c;
        }
        for(int k=1;k<=n;k++){
            for(int j=1;j<=n;j++){
                for(int i=1;i<=n;i++){
                    if(dis[i][k]+dis[k][j]<dis[i][j]){
                        dis[i][j]=dis[i][k]+dis[k][j];
                    }
                }
            }
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(dis[i][j]<=d) addLink(i,j);
            }
        }
        printf("Case #%d: ",++cas);
        yes=false; fans=inf;
        DLX(0);
        if(yes==true) printf("%d\n",fans);
        else printf("-1\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值