HDU 3957 Street Fighter(DLX搜索)

题目:Street Fighter

题意:以街头霸王为背景的一个题。有N个人物,每个人物有一到两种模式,每种模式有他特定可以击败的一些特定模式的人物。

现在要选择一些人物,同时要确定他们的模式,使得这些人能够击败剩下其他人的所有模式。

求要选择的最少的人物数。一个人物只能选择一种模式。

还是采用DLX搜索来解。

因为人物最多25个,总的模式最多50。

可以将人物的模式重新编号。然后根据输入连接。这里要注意两点:

1、已选择的人物不用击败,反过来说明选择一个人物,他自己的所有模式就都击败了;

2、同一人物只能选择一种模式,所以要注意记录好那个人物被选了。

然后就还是个经典的重复覆盖问题了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 100;
const int M = 3000;
typedef pair<int,int> pii;
vector<pii> V[N];
int T, n, m, sz, ans;
int a[N], beat[N][2][10], mode[N][2][10], id[N][2];
int L[M], R[M], U[M], D[M], col[M], cha[M], row[N], S[N];
void init(){
    for(int i=0; i<=m; i++){
        L[i] = i-1;
        R[i] = i+1;
        U[i] = D[i] = i;
        S[i] = 0;
        row[i] = -1;
    }
    L[0] = m;
    R[m] = 0;
    sz = m+1;
}
void link(int ch, int r, int c){
    S[c]++;
    col[sz] = c;
    cha[sz] = ch;
    U[sz] = U[c];
    D[sz] = c;
    U[D[sz]] = D[U[sz]] = sz;
    if(~row[r]){
        R[sz] = R[row[r]];
        L[sz] = row[r];
        L[R[sz]] = R[L[sz]] = sz;
    }
    else{
        L[sz] = R[sz] = sz;
    }
    row[r] = sz;
    sz++;
}
bool use[N];
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;
    }
}
bool vis[N];
bool h(int res){
    if(res>=ans)    return 0;
    memset(vis, 0, sizeof(vis));
    for(int i=R[0]; i!=0; i=R[i]){
        if(!vis[i]){
            res++;
            if(res>=ans)    return 0;
            vis[i] = 1;
            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 dfs(int dep){
    if(!h(dep))    return;
    if(!R[0]){
        ans = dep;
        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]){
        if(use[cha[i]]) continue;
        use[cha[i]] = 1;
        remove(i);
        for(int j=L[i]; j!=i; j=L[j])   remove(j);
        dfs(dep+1);
        for(int j=R[i]; j!=i; j=R[j])   restore(j);
        restore(i);
        use[cha[i]] = 0;
    }
}
int solve(){
    init();
    ans = n;
    for(int i=1; i<=n; i++){
        for(int j=0; j<a[i]; j++){
            int x = id[i][j];
            for(int k=0; k<V[x].size(); k++){
                pii &p = V[x][k];
                link(i, x, id[p.first][p.second]);
            }
        }
    }
    memset(use, 0, sizeof(use));
    dfs(0);
    return ans;
}
int main(){
    scanf("%d", &T);
    for(int t=1; t<=T; t++){
        scanf("%d", &n);
        m = 0;
        int b, c, d;
        for(int i=1; i<=n; i++){
            scanf("%d", a+i);
            for(int j=0; j<a[i]; j++){
                id[i][j] = ++m;
                V[m].clear();
                scanf("%d", &b);
                for(int k=0; k<b; k++){
                    scanf("%d %d", &c, &d);
                    V[m].push_back(make_pair(++c,d));
                }
                for(int k=0; k<a[i]; k++){
                    V[m].push_back(make_pair(i, k));
                }
                sort(V[m].begin(), V[m].end());
            }
        }
        printf("Case %d: %d\n", t, solve());
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值