[状压DP]TCO 2016 Fianl Div 1 Easy MealPlan

来源:互联网 发布:淘宝客点击数少 编辑:程序博客网 时间:2024/06/10 21:59

Description

给定四个集合S1,S2,S3,S4
每次从四个集合里分别选出一个数组成可重集合S
问可形成的S的数量。

Solution

考虑这样的状态:
当前已考虑前i种元素,从j个集合中选取了数。
把同一元素在四个集合中的出现情况压成20,21,22,23
再考虑把16种情况压成216
每次只要枚举本次要选取的状态,从当前状态的子集转移,保证不会从同一集合中多选就好了。

#include <bits/stdc++.h>using namespace std;class MealPlan{    public:    map<int, long long> mp, dp, dp1;    long long ans;    inline int BitCount(int x) {        int cnt = 0;        for (; x; x -= x & -x) ++cnt;        return cnt;    }    long long countDistinct(vector<int> a, vector<int> b, vector<int> c, vector<int> d) {        for (int i : a) mp[i] |= 1;        for (int i : b) mp[i] |= 2;        for (int i : c) mp[i] |= 4;        for (int i : d) mp[i] |= 8;        dp1[1] = 1;        for (auto i: mp) {            dp = dp1;            int s = i.second;            for (auto j: dp1) {                int x = j.first;                long long res = j.second;                for (int k = 1; k <= 4; k++) {                    int y = 0;                    for (int t = s; t; t = s & (t - 1)) {                        if (BitCount(t) == k) {                            for (int p = 0; p < 16; p++)                                if (!(p & t) && (x >> p & 1))                                    y |= 1 << (p | t);                        }                    }                    if (y) dp[y] += res;                }            }            dp1 = dp;        }        for (auto x: dp)            if (x.first >> 15 & 1)                ans += x.second;        return ans;    }};MealPlan S;vector<int> p[4];int n, x;int main(void)  {    freopen("1.in", "r", stdin);    for (int i = 0; i < 4; i++) {        cin >> n;        for (int j = 0; j < n; j++) {            cin >> x; p[i].push_back(x);        }    }    cout << S.countDistinct(p[0], p[1], p[2], p[3]) << endl;    return 0;}
原创粉丝点击