hdu 4735 Little Wish~ lyrical step~ (2013成都网络赛H题)(重复覆盖)

题意:有一个棵树,上面有n个结点。每个节点有住着一个孩子,男生或者女生,如果女生在一个D的范围内有男生则说明被保护,问你最少交换几个孩子的位置,使得所有女生都受保护,如果无法达成,输出-1。

思路:这道题目可以转化为重复覆盖的模型,男生结点为1,女生结点为0,则行为这个结点如果为1的话,可以那几个结点的人,包括自己,列为n个人,则可以使用DLX来解决。因为只有50个点,所以树上最短距可以用floyd预处理

但是这里要注意几点:

1)求得并不是最小的重复覆盖,而是在一定数目条件下,交换人数最少的次数,因此,要搜遍整棵树(也就是搜完所有可能),而不能使用迭代A*的方法。

2)如果完全搜整棵树会TLE,因此要加入一些剪枝,剪枝为,如果当前需要交换的节点数大于限制,则直接return.


代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define inf 0x3f3f3f
using namespace std;
const int maxn=60;
const int maxr=maxn*maxn+maxn;
int n,D;
int child[maxn];
int boys;
bool flag;
int map[maxn][maxn];
void init_m()//初始化图
{
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
        {
            if(i==j)
            {
                map[i][j]=0;
            }
            else
            {
                map[i][j]=inf;
            }
        }
}
void floyd()
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                if(map[i][k]+map[k][j]<map[i][j])
                {
                    map[i][j]=map[i][k]+map[k][j];
                }
            }
}
struct node
{
    int l,r;
    int u,d;
    int s,c;
};
node dlx[maxr];
int h[maxn];
int limit;
int Q[maxn];
int X[maxr];
int hash[maxn];
int head,size;
void init(int r,int c)
{
    head=0;
    for(int i=0; i<=c; i++)
    {
        dlx[i].s=0;
        dlx[i].u=dlx[i].d=i;
        dlx[i].r=i+1;
        dlx[i+1].l=i;
    }
    size=c;
    dlx[c].r=0;
    while(r)
    {
        h[r--]=-1;
    }
}
void link(int r,int c)
{
    int tmp=++size;
    dlx[c].s++;
    dlx[tmp].c=c;
    X[tmp]=r;
    dlx[tmp].d=dlx[c].d;
    dlx[tmp].u=c;
    dlx[dlx[c].d].u=tmp;
    dlx[c].d=tmp;
    if(h[r]<0)
    {
        h[r]=dlx[tmp].l=dlx[tmp].r=tmp;
    }
    else
    {
        dlx[tmp].r=dlx[h[r]].r;
        dlx[tmp].l=h[r];
        dlx[dlx[h[r]].r].l=tmp;
        dlx[h[r]].r=tmp;
    }
}
void remove(int x)
{
    for(int i=dlx[x].d; i!=x; i=dlx[i].d)
    {
        dlx[dlx[i].r].l=dlx[i].l;
        dlx[dlx[i].l].r=dlx[i].r;
        dlx[dlx[i].c].s--;
    }
}
void resume(int x)
{
    for(int i=dlx[x].u; i!=x; i=dlx[i].u)
    {
        dlx[dlx[i].r].l=i;
        dlx[dlx[i].l].r=i;
        dlx[dlx[i].c].s++;
    }
}
int hh()
{
    int ret=0;
    memset(hash,0,sizeof(hash));
    for(int i=dlx[head].r; i!=head; i=dlx[i].r)
    {
        if(hash[i]==false)
        {
            hash[i]=true;
            ret++;
            for(int j=dlx[i].d; j!=i; j=dlx[j].d)
            for(int k=dlx[j].r; k!=j; k=dlx[k].r)
            {
                hash[dlx[k].c]=true;
            }
        }
    }
    return ret;
}
void dlx_dfs(int k)
{
    if(k+hh()>boys)//最大的限制为男生总数
    {
        return;
    }
    int ssd=0;
    for(int i=0;i<k;i++)
    {
        ssd+=child[Q[i]];
    }
    if(k-ssd>=limit) return;//剪枝判断
    if(dlx[head].r==head)
    {
        flag=true;
        limit=k-ssd;
        return;
    }
    int minn=n+2;
    int tt;
    for(int i=dlx[head].r; i!=head; i=dlx[i].r)
    {
        if(minn>dlx[i].s)
        {
            minn=dlx[i].s;
            tt=i;
        }
    }
    for(int i=dlx[tt].d; i!=tt; i=dlx[i].d)
    {
        Q[k]=X[i];
        remove(i);
        for(int j=dlx[i].r; j!=i; j=dlx[j].r)
        {
            remove(j);
        }
        dlx_dfs(k+1);
        for(int j=dlx[i].l; j!=i; j=dlx[j].l)
        {
            resume(j);
        }
        resume(i);
    }
    return;
}
int main()
{
   // freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    int cas=1;
    while(t--)
    {
        printf("Case #%d: ",cas++);
        scanf("%d%d",&n,&D);
        boys=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&child[i]);
            if(child[i]==1)
            {
                boys++;
            }
        }
        init_m();
        int a,b,c;
        for(int i=0;i<n-1;i++)//正反建图
        {
            scanf("%d%d%d",&a,&b,&c);
            map[a][b]=c;
            map[b][a]=c;
        }
        floyd();
        init(n,n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                if(map[i][j]<=D)
                {
                    link(i,j);
                }
            }
            flag=false;
            limit=inf;
        dlx_dfs(0);
        if(!flag)
        {
            printf("-1\n");
        }
        else
        {
            printf("%d\n",limit);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值