标签: 状压DP

  • 洛谷2396 yyy loves Maths VII

    又是一道包题。

    直接状压DP即可。

    注意一下常数优化

    // luogu-judger-enable-o2
    #include <iostream>
    
    using int_t = unsigned int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 1e9 + 7;
    
    inline int_t lowbit(int_t x)
    {
        return x & (-x);
    }
    
    int_t arr[30];
    int_t dp[1 << 24];
    // int_t sum[1 << 20];
    int_t to[1 << 24];
    int_t n, m;
    int_t bad[4];
    char bit[1 << 24];
    int main()
    {
        cin >> n;
        for (int_t i = 0; i < n; i++)
            cin >> arr[i];
        cin >> m;
        for (int_t i = 0; i < m; i++)
            cin >> bad[i];
        for (int_t i = 0; i < 24; i++)
            bit[1 << i] = i;
        for (int_t i = 1; i < (1 << n); i++)
        {
            to[i] = to[i - lowbit(i)] + arr[bit[lowbit(i)]];
            if (to[i] == bad[0] || to[i] == bad[1])
                dp[i] = -1;
        }
        dp[0] = 1;
        for (int_t i = 1; i < (1 << n); i++)
        {
            if (dp[i] == -1)
            {
                dp[i] = 0;
                continue;
            }
            int_t curr = i;
            while (curr)
            {
                int_t bit = lowbit(curr);
    
                dp[i] = (dp[i] + dp[i ^ bit]);
                dp[i] -= (dp[i] > mod) ? mod : 0;
                curr -= bit;
            }
        }
        cout << dp[(1 << n) - 1] << endl;
        return 0;
    }

     

  • NOIP2017 宝藏

    $$ f\left( vtx,len,set \right) \text{表示当前在点}vtx\text{,已经挖了长度为}len\text{的道路,要去挖通}set\text{这些点的最小代价} \\ \text{转移:} \\ \text{对于每一个}vtx\text{,枚举出边,对于每一个出边}\left( vtx,to \right) \text{,枚举要从这个点去挖的子集}\left( \text{这个子集必须要包含}to \right) \text{,然后统计上挖这条边的代价。} \\ \\ f\left( vtx,len,set \right) =MIN_{S\subset \ set\ and\ vtx\ \notin \ S}MIN_{edge\ \left( vtx,to \right) \ and\ to\in \ S}f\left( to,len+\text{1,}S \right) \ +\ f\left( vtx,len,S^{set} \right) \ +\left( len+1 \right) \times weight\left( vtx,to \right) \ \\ \text{边界条件:} \\ f\left( vtx,len,0 \right) =0 \\ \text{挖空集的代价为}0 \\ $$

     

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <vector>
    #include <cstring>
    #include <string>
    typedef  int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t LARGE = 20;
    const int_t INF = 0x3f3f3f3f;
    
    
    int_t n,m;
    
    int_t graph[LARGE + 1][LARGE + 1];
    int_t memory[LARGE + 1][LARGE + 1][1 << 13];
    bool visited[LARGE + 1][LARGE + 1][1 << 13];
    int_t* subsets[1 << 13];
    struct Edge{
        int_t to;
        int_t weight;
        int_t next;
        Edge(int_t to = 0,int_t weight = 0,int_t next = 0){
            this -> to = to;
            this -> weight = weight;
            this -> next = next;
        }
    } edges[LARGE * LARGE + 1];
    int_t next = 1;
    int_t head[LARGE + 1];
    void pushEdge(int_t from,int_t to,int_t weight){
        int_t id = next++;
        edges[id] = Edge(to,weight,head[from]);
        head[from] = id;
    }
    
    int_t search(int_t vtx,int_t len,int_t set) {
    	//要去挖空集
    	if(set == 0) return memory[vtx][len][set] = 0;
    	//已经挖了长度为n - 1的链
    	if(len == n - 1) {
    		if(set != 0) return memory[vtx][len][set] = INF;
    	}
    	int_t& result = memory[vtx][len][set];
    	if(visited[vtx][len][set]) return result;
    	visited[vtx][len][set] = true;
    	//不要把结果在记忆化返回前赋值 
    	result = INF;
    	for(int_t ptr = head[vtx];ptr != 0; ptr = edges[ptr].next) {
    	    int_t to = edges[ptr].to;
    	    int_t weight = edges[ptr].weight;
    		//枚举出边
    		if(to == vtx  || (set & (1 << to)) == 0) continue;
    		//枚举要挖的子集
    		//不能去挖空集
    		for(int_t k = 1; k <= subsets[set][0]; k ++) {
    			int_t x = subsets[set][k];
    			if(x & (1 << to)) {
    				result = std::min(result,search(to , len + 1, x & (~(1 << to))) + weight * (len + 1) + search(vtx,len,set & (~(x))));
    			}
    		}
    	}
    	return result;
    }
    
    int main() {
    	cin >> n >> m;
    	for(int_t i = 0; i < n; i++) {
    		for(int_t j = 0; j < n; j++) {
    			graph[i][j] = INF;
    		}
    	}
    	for(int_t i = 1; i <= m; i ++) {
    		int_t from,to,weight;
    		cin >> from >> to >> weight;
    		from -= 1;
    		to -= 1;
    		graph[from][to] = std::min(graph[from][to],weight);
    		graph[to][from] = graph[from][to];
    	}
    	for(int_t i = 0;i < n;i++){
    	    for(int_t j = 0;j < n;j++){
    	        if(graph[i][j] < INF && i != j){
    	            pushEdge(i,j,graph[i][j]);
                }
            }
        }
        static int_t subset[1 << 13];
        for(int_t i = 1 ;i < (1 << n);i ++){
            
            for(int_t j = 1;j <= i;j++){
                if((j & i) == j) subset[++subset[0]] = j;
            }
            std::sort(subset + 1,subset + 1 + subset[0]);
            int_t* tail = std::unique(subset + 1,subset + 1 + subset[0]);
            subsets[i] = new int_t[tail - subset];
            memcpy(subsets[i],subset,sizeof(int_t) * (tail - subset));
            subset[0] = 0;
        }
    	int_t result = INF;
    	for(int_t i = 0; i < n; i++) {
    	    int_t temp = search(i,0,((1 << n) - 1) ^ (1 << i));
    		result = std::min(result,temp);
    	}
    	cout << result << endl;
    	return 0;
    }

     

  • NOIP2016 愤怒的小鸟

    $$ \text{设}state\text{是还没死的猪的集合。} \\ \text{转移时,首先考虑一条抛物线只打一头猪的情况,然后转移。} \\ \text{然后再考虑两头猪连成一条抛物线的情况,然后判断一下其他哪些猪也在这条抛物线上} \\ \text{然后转移。} \\ \text{这样做的复杂度是}O\left( T\times n^3\times 2^n \right) \\ \text{在}luogu\text{会}TLE\text{两个点。} \\ \text{考虑优化枚举两头猪连成一条抛物线的过程。} \\ \text{先处理出来一个数组}sets\left[ i \right] \left[ j \right] \text{,表示经过第}i\text{头和第}j\text{头猪的抛物线可以打下来的猪的集合。} \\ \text{然后转移的复杂度可以优化到}O\left( T\times n^2\times 2^n \right) \\ \text{然后}luogu\ AC,LOJ\ AC $$

     

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cstdio>
    #include <cmath>
    #include <assert.h>
    using int_t = long long int;
    
    using real_t = double;
    
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = (1 << 20) - 1;
    const int_t INF = 0x7fffffff;
    const real_t EPS = 1e-6;
    template <class T>
    T fabs(T x)
    {
        if (x < 0)
            return -x;
        return x;
    }
    template <class A, class B>
    auto fcmp(A a, B b) -> decltype(a - b)
    {
        if (fabs(a - b) < EPS)
            return 0;
        return a - b;
    }
    struct Processor
    {
        struct Pig
        {
            real_t x;
            real_t y;
            int_t bit;
        } pigs[20];
        int_t n, m;
        //经过i j 两个点抛物线所能打下来的猪的集合
        int_t sets[20][20];
        int_t memory[LARGE];
        bool visited[LARGE];
        int_t search(int_t state)
        {
            if (state == 0)
                return 0;
            if (visited[state])
                return memory[state];
            auto &result = memory[state];
            visited[state] = true;
            result = INF;
            std::vector<Pig> alive;
            for (int_t i = 0; i < n; i++)
            {
                if (state & (1 << i))
                {
                    //考虑一条抛物线打一头猪
                    result = std::min(search(state ^ (1 << i)) + 1, result);
                    alive.push_back(pigs[i]);
                    alive.back().bit = i;
                }
            }
            //考虑任何两头猪构成的抛物线
            for (int_t i = 0; i < alive.size(); i++)
            {
                for (int_t j = i + 1; j < alive.size(); j++)
                {
                    if (sets[alive[i].bit][alive[j].bit])
                    {
                        result = std::min(result, search(state & (~sets[alive[i].bit][alive[j].bit])) + 1);
                    }
                }
            }
            return result;
        }
    
        int_t processs()
        {
    
            cin >> n >> m;
            for (int_t i = 0; i < n; i++)
            {
                cin >> pigs[i].x >> pigs[i].y;
            }
            for (int_t i = 0; i < n; i++)
            {
                for (int_t j = i + 1; j < n; j++)
                {
                    auto &curr = sets[i][j];
                    curr = 0;
                    real_t x1 = pigs[i].x, y1 = pigs[i].y;
                    real_t x2 = pigs[j].x, y2 = pigs[j].y;
                    if (x1 == x2)
                        continue;
                    real_t a = ((y1 / x1) - (y2 / x2)) / (x1 - x2);
                    if (a >= 0)
                        continue;
                    real_t b = ((y1 / (x1 * x1)) - (y2 / (x2 * x2))) / ((1 / x1) - (1 / x2));
                    for (int_t k = 0; k < n; k++)
                    {
                        auto &currp = pigs[k];
                        if (fabs(currp.y - (currp.x * currp.x * a + currp.x * b)) <= EPS)
                        {
                            curr ^= (1 << k);
                        }
                    }
                }
            }
            std::fill(visited, visited + LARGE, false);
            return search((1 << n) - 1);
        }
    };
    
    int main()
    {
        int_t T;
        cin >> T;
        for (int_t i = 1; i <= T; i++)
        {
            auto p = new Processor;
            cout << p->processs() << endl;
            delete p;
        }
        return 0;
    }