ICPC 2017 Japan Domestic Making Lunch Boxes 思维+二进制枚举+简单dp

8833: Making Lunch Boxes

时间限制: 15 Sec  内存限制: 128 MB
提交: 19  解决: 4
[提交] [状态] [讨论版] [命题人:admin]

题目描述

Taro has been hooked on making lunch boxes recently. Taro has obtained a new lunch box recipe book today, and wants to try as many of the recipes listed in the book as possible. 
Enough of the ingredients for all the recipes are at hand, but they all are in vacuum packs of two. If only one of them is used leaving the other, the leftover will be rotten easily, but making two of the same recipe is not interesting. Thus he decided to make a set of lunch boxes, each with different recipe, that wouldn't leave any unused ingredients. 
Note that the book may include recipes for different lunch boxes made of the same set of ingredients. 
How many lunch box recipes can Taro try today at most following his dogma? 

输入

The input consists of at most 50 datasets, each in the following format. 
n m
b1,1...b1,m 
 ... 
bn,1...bn,m 
The first line contains n, which is the number of recipes listed in the book, and m, which is the number of ingredients. Both n and m are positive integers and satisfy 1 ≤ n ≤ 500, 1 ≤ m ≤ 500 and 1 ≤ n × m ≤ 500. The following n lines contain the information for each recipe with the string of length m consisting of 0 or 1. bi,j implies whether the i-th recipe needs the j-th ingredient. 1 means the ingredient is needed for the recipe and 0 means not. Each line contains at least one 1. 
The end of the input is indicated by a line containing two zeros. 

输出

For each dataset, output the maximum number of recipes Taro can try. 

样例输入

4 3
110
101
011
110
7 1
1
1
1
1
1
1
1
4 5
10000
01000
00100
00010
6 6
111111
011000
100000
000010
100001
100100
0 0

样例输出

3
6
0
6

题意:给n个菜,每个菜需要m种原料中的几种,每个原料必须使用偶数个,求能做的最多的菜数

比赛时队友想到了用异或统计,也就这么些,之后一直想不出来怎么枚举

还搜不到题解,痛苦

盯着别人AC的代码,看了好一会才看懂

接下来说解题思路:

首先因为题里保证 n * m 不超过500,n和m就必定有一个比较小,那么就哪个小枚举哪个

(22这个范围,500开根号,当然是别人告诉我的)

当n比较小的时候,枚举结果的状态,最大2^{22},差不多1e6,每次判断选出来的菜里每种材料是不是偶数个

当m比较小的时候,枚举菜所需要的材料的状态,差不多也是1e6,二维数组开不下更大的,所以只存两个值,每次更新即可

代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn = 505;
const int inf = 1e9;

int n, m;
int mapp[maxn][maxn];
int bit[maxn];
int dp[2][1 << 22];

void Bit() {
    int size = 1 << n;        //枚举所有可能的结果
    int cnt = 0, ans = 0;
    for (int i = 0; i < size; i++) {
        cnt = 0;           
        for (int j = 0; j < m; j++) bit[j] = 0;
        for (int j = 1; j <= n; j++) {
            if ((i & (1 << (j - 1))) == 0) continue;
            cnt++;
            for (int k = 0; k < m; k++) bit[k] ^= mapp[j][k];  
        }
        int f = 0;
        for (int j = 0; j < m; j++) f |= bit[j];
        if (!f) ans = max(ans, cnt);
    }
    printf("%d\n", ans);
}

void DP() {
    int size = 1 << m;
    for (int i = 1; i <= n; i++) {
        bit[i] = 0;
        for (int j = 0; j < m; j++)
            bit[i] |= (mapp[i][j] << j);  //bit[i]就是第i种菜所需的材料,也就是mapp[i][(1~m)]
    }
    for (int i = 0; i < size; i++)
        dp[0][i] = dp[1][i] = -inf;
    dp[0][0] = 0;
    for (int i = 0; i < n; i++) {        //利用是否是2的倍数,只保留上一次的值,每次更新
        for (int j = 0; j < size; j++) {
            dp[(i + 1) % 2][j ^ bit[i + 1]] = max(dp[(i + 1) % 2][j ^ bit[i + 1]], dp[i % 2][j] + 1);
            dp[(i + 1) % 2][j] = max(dp[(i + 1) % 2][j], dp[i % 2][j]);
        }
    }
    printf("%d\n", dp[n % 2][0]);
}

int main() {
    while (~scanf("%d%d",&n,&m)) {
        if (n == 0 && m == 0) break;
        char ch;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < m; j++) {
                cin>>ch;
                mapp[i][j] = ch - '0';
            }
        }
        if (n <= 22) { //分情况枚举
            Bit();
        } else {
            DP();
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/renzijing/article/details/82320829