zoukankan      html  css  js  c++  java
  • 状态压缩子集问题

    描述:给定一个n(1≤n≤10)个数(可正可负)的集合,求一个划分方法,使得所有划分块的代价和最小。其中每个分块的代价和最小。其中每个块的代价为块内数字的和的平方。

    分析:因为看到n最大为10,所以可以用状态压缩DP,复杂度最高为O(2^10*2^10)

    设dp[i]表示状态为i的时候的最小代价和。

    可以推出dp[X] = min(dp[Y] + dp[Z] | Y∪Z = X && Y∩Z = ∅}

    初始的时候dp[X] = sum{a[i] | i在X集合中}^2.

    所以可以写出

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <string>
     5 #include <algorithm>
     6 using namespace std;
     7 int n;
     8 #define INF 0x3f3f3f
     9 int a[11];
    10 int dp[1030];
    11 int main(){
    12     while(cin>>n){
    13         for(int i = 1; i <= n; i++){
    14             scanf("%d", &a[i]);
    15         }
    16         for(int i = 0; i < (1<<10); i++){
    17             int sum = 0;
    18             int temp = i;
    19             int co = 1;
    20             while(temp!=0){
    21                 int x = temp&1;
    22                 if(x == 1){
    23                     sum += a[co];
    24                 }
    25                 co++;
    26                 temp = temp/2;
    27             }
    28             dp[i] = sum*sum;
    29         }
    30             
    31         for(int i = 0; i < (1<<n); i++){
    32             for(int j = 0; j < i; j++){
    33                 if((i&j)==0){ //没有重合元素. 
    34                     dp[i|j] = min(dp[i|j], dp[i]+dp[j]);            
    35                 }
    36             }
    37         }
    38         cout<<dp[(1<<n)-1]<<endl;
    39         
    40     }
    41     
    42     return 0;
    43 }

    但是可以进一步优化算法。

    Z可以表示为Y^X,Y属于X可以表示(Y&X) = Y.

    所以可以写为dp[X] = min(dp[Y] + dp[X^Y] | X&Y=Y])

    枚举的时候优化为O(3^n的算法)

    for(y = (x-1)&x; y > 0; y = (y-1)&x)

    因为(x-1)&x表示将x的第一位1变为0.

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <string>
     5 #include <algorithm>
     6 using namespace std;
     7 int n;
     8 #define INF 0x3f3f3f
     9 int a[11];
    10 int dp[1030];
    11 int main(){
    12     while(cin>>n){
    13         for(int i = 1; i <= n; i++){
    14             scanf("%d", &a[i]);
    15         }
    16         for(int i = 0; i < (1<<10); i++){
    17             int sum = 0;
    18             int temp = i;
    19             int co = 1;
    20             while(temp!=0){
    21                 int x = temp&1;
    22                 if(x == 1){
    23                     sum += a[co];
    24                 }
    25                 co++;
    26                 temp = temp/2;
    27             }
    28             dp[i] = sum*sum;
    29         }
    30             
    31         for(int i = 0; i < (1<<n); i++){
    32             for(int j = (i-1)&i; j>0; j = (j-1)&i){
    33                 dp[i] = min(dp[i], dp[j]+dp[i^j]);            
    34                 
    35             }
    36         }
    37         cout<<dp[(1<<n)-1]<<endl;
    38         
    39     }
    40     
    41     return 0;
    42 }

    状态压缩 位运算的一些知识

    左移操作:1<<X,表示把1向左移动X位。

          X<<1,表示把X的每一位向左移动1位。(看做乘2)右边不够的用0添上。

    右移类似。

    获取一个或多个固定位的值

    X&(1<<j)表示获取X从右边数第j-1位的值。

    把一个或多个固定位的位置置0。

    x&(~(1<<j))

    取反则用异或

    x&(x-1)表示把x的第一个出现的1变成0.

  • 相关阅读:
    How To Build CyanogenMod Android for smartphone
    CentOS安装Code::Blocks
    How to Dual boot Multiple ROMs on Your Android SmartPhone (Upto Five Roms)?
    Audacious——Linux音乐播放器
    How to Dual Boot Multiple ROMs on Your Android Phone
    Everything You Need to Know About Rooting Your Android Phone
    How to Flash a ROM to Your Android Phone
    什么是NANDroid,如何加载NANDroid备份?
    Have you considered compiled a batman-adv.ko for android?
    BATMAN—Better Approach To Mobile Adhoc Networking (B.A.T.M.A.N.)
  • 原文地址:https://www.cnblogs.com/titicia/p/4347910.html
Copyright © 2011-2022 走看看