hdu 3957 Street Fighter 重复覆盖于+精确覆盖 DancingLink

Street Fighter

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1257    Accepted Submission(s): 472


Problem Description
Street Fighter is a popular Fighting game. In the game, you could choose many roles to fight. 
Each role has some models, in different model the role has different super skill. Such as Ryu, he has two model -- "Metsu Hadoken" and "Metsu Shoryuken". In "Metsu Hadoken" model, Ryu could beat Chun-Li easyly and in "Metsu Shoryuken" he could beat Ken.

Giving the information of which role in which model could beat which role in which model. Your task is choosing minimum roles in certain model to beat other roles in any model.(each role could only be chosen once)
 

Input
The first line is a number T(1<=T<=30), represents the number of case. The next T blocks follow each indicates a case.
The first line of each case contains a integers N(2<=N<=25), indication the number of roles. (roles numbered from 0 to N - 1).
Then N blocks follow, each block contain the information of a role.
The first of each block contains a integer M(1<=M<=2), indication the number of model of this role.(models numbered from 0 to M - 1)
Then M lines follow, each line contain a number K(1<=K<=10), then K pairs integers(role id and model id) follow, indicating the current role in this model could beat that role in such model.
 

Output
For each case, output the number of case and the minimum roles have to choose to reach the goal.(as shown in the sample output)
 

Sample Input
  
  
2 2 2 1 1 0 1 1 1 2 1 0 0 1 0 1 4 2 2 1 0 1 1 1 2 0 2 2 3 0 0 1 1 2 0 2 2 0 0 0 1 1 1 0 2 2 2 0 2 1 1 1 0
 

Sample Output
  
  
Case 1: 2 Case 2: 2
Hint
In the first sample, you must select all of roles, because one role couldn't beat the other role in any model. In the second sample, you can select role 0 with its model 0, and role 3 with its model 0, then role 1 and role 2 will to be defeated no matter which model they use.
 

Author
NotOnlySuccess
 

Source
 

题意:

    有N个英雄,最多有两个状态,每个状态的英雄都能打败一些其他状态的英雄。问,至少选出几个英雄就能打败剩下的英雄的所有状态。

做法:

       容易想到的是选出一些状态的英雄打败其他所有英雄,可以用重复覆盖的方法建模型。但是一个英雄只能选一个状态,所以不能是有限制的选择行的。因此还要添加限制条件。同一种英雄的不同状态,设置一个列,如果该列的对应的这个英雄的状态的行上为1.除了在每个状态的英雄能打败的状态的英雄上设置1外,一个英雄的状态能打败这个英雄的其他状态。然后搜索的时候 不对限制列的进行f()函数的判断,优化的时候也不在这些限制的列上选择。只有在选了一行后,才处理限制列,把对应的其他行给删除掉。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<algorithm>
#define maxn 100
#define maxm 1000
using namespace std;
//十字链表指针(4)
int up[maxm],down[maxm],lef[maxm],righ[maxm];
//行指针,记录在该列为1的行数,覆盖矩阵,行属性,列属性
int head[maxn],sum[maxn],row[maxm],colume[maxm];
bool v[maxn];
//行数,列数,选取行数上线,矩阵点编号
int maxmum,size;
//初始化r行,c列
void prepare(int r,int c){
    for(int i=0;i<=c;++i){
        sum[i]=0;
        up[i]=down[i]=i;
        lef[i+1]=i;
        righ[i]=i+1;
    }
    righ[c]=0;
    size = c;
    memset(head,-1,sizeof(head));
}

//移除某列
void remove(int c){
    for(int i = down[c];i != c;i = down[i])
        lef[righ[i]]=lef[i],righ[lef[i]]=righ[i];
}
//插入某列
void resume(int c){
    for(int i=up[c];i!=c;i=up[i])
        lef[righ[i]]=righ[lef[i]]=i;
}
//删除列及其相应的行
void remove1(int c){
    int i,j;
    lef[righ[c]] = lef[c];
    righ[lef[c]] = righ[c];
    for(i = down[c]; i != c; i = down[i]){
        for(j = righ[i]; j != i; j = righ[j]){
            up[down[j]] = up[j];
            down[up[j]] = down[j];
            sum[colume[j]]--;
        }
    }
}

//恢复列及其相应的行
void resume1(int c){
    int i,j;
    righ[lef[c]] = c;
    lef[righ[c]] = c;
    for(i = down[c]; i != c; i = down[i]){
        for(j = righ[i]; j != i; j = righ[j]){
            up[down[j]] = j;
            down[up[j]] = j;
            sum[colume[j]]++;
        }
    }
}
int flag;
//代价函数
int f(){
    int i,j,c,ret=0;
    for(c = righ[0];c;c = righ[c])v[c]=1;
    for(c = righ[0];c;c = righ[c])
    if(v[c] && c <= flag)
        for(++ret,v[c]=0,i=down[c];i!=c;i=down[i]){
            for(j=righ[i];j!=i;j=righ[j])v[colume[j]]=0;
        }
    return ret;
}
//执行第k层
bool Dance(int k){
    if(k+f()>= maxmum)return 0;
    //righ[0]=0表示覆盖完毕
    if(!righ[0] || righ[0] > flag) {
        //cout<<k<<" k"<<endl;
        maxmum = k;
        return 1;
    }
    int i,j,c,tmp=maxm;
    for(i=righ[0];i <= flag;i=righ[i])
        if(sum[i]<tmp)tmp=sum[c=i];
    for(i=down[c];i!=c;i=down[i]){
        remove(i);
        for(j=righ[i];j!=i;j=righ[j])
            if(colume[j] <= flag)
                remove(j);
        for(j=righ[i];j!=i;j=righ[j])
            if(colume[j] > flag)
                remove1(colume[j]);
        Dance(k+1);
        for(j=lef[i];j!=i;j=lef[j])
            if(colume[j] > flag)
                resume1(colume[j]);
        for(j=lef[i];j!=i;j=lef[j])
            if(colume[j] <= flag)
                resume(j);
        resume(i);
    }
    return 0;
}
//插入r行c列
void Link(int r,int c){
    ++sum[colume[++size] = c];
    down[size] = down[c];
    up[down[c]] = size;
    up[size] = c;
    down[c] = size;
    row[size] = r;
    if(head[r] < 0)head[r] = lef[size] = righ[size] = size;
    else{
        righ[size] = righ[head[r]];
        lef[righ[head[r]]] = size;
        lef[size] = head[r];
        righ[head[r]] = size;
    }
}


int ma[100][200];
int ty[50];
int su[50];
int kil[50];
int rol[50][50][2];
int main(){
    int t,n,m;
    scanf("%d",&t);
    for(int tt = 1; tt<= t;tt++){
        scanf("%d",&n);
        memset(ma,0,sizeof(ma));
        int cnt = 0;
        for(int i = 0;i < n; i++){
            scanf("%d",&ty[i]);
            for(int l = 0;l < ty[i]; l++){
                scanf("%d",&kil[cnt]);
                for(int j = 0;j < kil[cnt];j++)
                    scanf("%d%d",&rol[cnt][j][0],&rol[cnt][j][1]);
                cnt++;
            }
        }
        su[0] = ty[0];
        for(int i = 1; i < n; i++)
            su[i] = su[i-1] + ty[i];
        for(int i = 0;i < cnt; i++){
            for(int j = 0;j < kil[i];j++){
                int u = rol[i][j][0];
                u = su[u] - ty[u] + rol[i][j][1];
                ma[i][u] = 1;
            }
        }
        flag = cnt ;
        for(int i = 0;i < n; i++){
            for(int j = 0;j < ty[i]; j++){
                int u = su[i] - ty[i] + j;
                ma[u][cnt+i] = 1;
                for(int k = 0;k < ty[i]; k++){
                    int vv = su[i] - ty[i] + k;
                    ma[u][vv] = 1;
                }
            }
        }
        prepare(cnt ,cnt + n);
        for(int i = 0;i < cnt; i++){
            for(int j = 0;j < cnt +n; j++){
                if(ma[i][j])
                    Link(i+1,j+1);
            }
        }
        maxmum = n;
        int x = Dance(0);
        printf("Case %d: %d\n",tt,maxmum);
    }
    return 0;
}
/*  剪枝说明
    1,选取一列未覆盖的列,选取在该列上为1的所有行,把这些行对应的所有列都覆盖
    2,重复1直到所有列被覆盖得到f(),可知最少需要f()行才能覆盖所有列
    输出结果:在Dance中如果返回1则记录该点
*/





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值