题目链接: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;
}