2014 GCJ Round 1A

目前只会做A,B,C后面尽量补上。

今年的实力离拿T还太远, 只能寄希望于明年了, sad。。

题目链接:GCJ|2014|Round1A


A:题意:给出n个01串, 每个字符串的长度为L, 现在可以同时可以对初始n个串的同一位置进行取反操作,问要操作多少次能达到目标串。

分析:小数据范围是:n,L <= 10, 联想到资格赛的C题(在我之前的blog出现过:资格赛C题)学到一种关于在n个数里快速求K个数组合的方式,我就开始枚举了。

太弱了写疵了,各种WA。。(想起前段时间的某次考试,一辈子的阴影么?) 最后还是找出了错误。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
int d[12];
int c[12];
int n, L;

bool gao(int *a, int n){
    for(int i = 0; i < n; ++i){
        if(a[i] != c[i])
            return 0;
    }
    return 1;
}

int main(){
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    int T, cases = 1;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &L);
        for(int i = 0; i < n; ++i){
            char s[12];
            scanf("%s", &s);
            int tmp = 0;
            for(int j = 0; j < L; ++j){
                tmp = tmp*2 + s[j] - '0';
            }
            d[i] = tmp;
        }

        for(int i = 0; i < n; ++i){
            char s[12];
            scanf("%s", &s);
            int tmp = 0;
            for(int j = 0; j < L; ++j){
                tmp = tmp*2 + s[j] - '0';
            }
            c[i] = tmp;
        }
        sort(d, d + n);
        sort(c, c + n);

        printf("Case #%d: ", cases++);
        if(gao(d, n)){
            printf("0\n");
            continue;
        }
        int ans = 0;

        for(int k = 1; k <= L; ++k){
            int e[11];
            for(int i = 0; i < n; ++i) e[i] = d[i];

            int comb = (1 << k) - 1;
            for(int i = 0; i < k; ++i){
                for(int j = 0; j < n; ++j)
                    e[j] ^= (1 << (i));
            }

            while(comb < (1 << (L))){
                sort(e, e + n);
                if(gao(e, n)){
                    ans = k;
                    break;
                }

                int x = comb & -comb, y = comb + x;
                comb = ((comb & ~y) / x >> 1) | y;

                for(int i = 0; i < n; ++i) e[i] = d[i];
                for(int i = 0; i < L; ++i){
                    for(int j = 0; j < n; ++j){
                        e[j] ^= (comb & (1 << i));
                    }
                }
            }
            if(ans != 0) break;
        }

        if(ans != 0) printf("%d\n", ans);
        else printf("NOT POSSIBLE\n");
    }
    return 0;
}

其实这题的正解是:枚举初始第一个串可以到达的目标串,这样可以求出对应的操作。根据操作去判断枚举是否合法即可, 时间复杂度为O(n^2*L)。对于大数据1 ≤ N ≤ 150,10 ≤ L ≤ 40,这样的复杂度也是能接受的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N = 210;
string x[210], y[210], z[210];

int main(){
    int T, cases = 1;
    scanf("%d", &T);
    while(T--){
        int n,m;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            cin >> x[i];
        for(int i = 1; i <= n; ++i)
            cin >> y[i];
        sort(y + 1, y + n + 1);

        int flip[200], ans = -1;
        for(int i = 1; i <= n; ++i){
            memset(flip, 0, sizeof(flip));
            int sum = 0;
            for(int j = 0; j < m; j++){
                if(y[i][j] != x[1][j]) flip[j] = 1;
                sum += flip[j];
            }

            for(int j = 1; j <= n; ++j){
                z[j] = "";
                for(int k = 0; k < m; ++k){
                    if(flip[k])
                        z[j] += '0' + (!(x[j][k] - '0'));
                    else z[j] += x[j][k];
                }
            }
            sort(z + 1, z + n + 1);

            bool flag = true;
            for(int j = 1; j <= n; ++j){
                if(z[j] == y[j]) continue;
                flag = 0;
                break;
            }

            if(flag){
                if(ans != -1)
                    ans = min(ans,sum);
                else ans = sum;
            }
        }

        if(ans == -1)
            printf("Case #%d: NOT POSSIBLE\n", cases++);
        else
            printf("Case #%d: %d\n",cases++, ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值