C/C++教程

【递归/dp】Acwing1243.糖果(IDA*+步调函数/状态压缩+01背包)

本文主要是介绍【递归/dp】Acwing1243.糖果(IDA*+步调函数/状态压缩+01背包),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目

题解

动态规划

用二进制表示每种味道尝了没有,如:一共有五种味道,吃了前三种——00111,吃了五种——11111
每包糖果都可以选或不选
dp(i,j) i代表前i包糖果, j代表所能到达的状态(吃了几种种味)
其实就是01背包问题
dp[i-1][j & (~w[i])]+1 意味着:选这包糖果,从前面未选这包的状态+1包
同时不用担心重复问题
前面:1 2 3
后面:3 4 5
当前:j & (~w[i]) 会把 3, 4,5位上的1化为0,可前面应该是1,2,3位都为1才有的选,但通过这种运算,前面:00001也有的选,00011也有的选,00111也有的选

未状压(数组需要开到100*(2^20)超过百万,内存空间不够)

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110;

int n,m,k;
int dp[N][1000],w[22];

int main()
{
    cin >> n >> m >> k;
    int t;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= k; ++j)
        {
            cin >> t;
            w[i] |= (1 << t-1);
        }
    }
    
    memset(dp,0x3f,sizeof dp);
    
    dp[0][0] = 0;
    int all  = (1 << m)-1;
    
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 0; j <= all; ++j)
        {
            dp[i][j] = min(dp[i-1][j],dp[i-1][j & (~w[i])]+1);
        }
    }
    if(dp[n][all] >= 0x3f3f3f3f)
        cout << -1 << endl;
    else
        cout << dp[n][all] << endl;
    return 0;
}

状压

#include <iostream>
#include <cstring>
using namespace std;

const int N = (1<<20)+10;

int n,m,k;
int dp[N],w[22];

int main()
{
    cin >> n >> m >> k;
    int t;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= k; ++j)
        {
            cin >> t;
            w[i] |= (1 << t-1);
        }
    }
    
    memset(dp,0x3f,sizeof dp);
    
    dp[0] = 0;
    int all  = (1 << m)-1;
    
    for(int i = 1; i <= n; ++i)
        for(int j = all; j >= 0; j--)
        {
            dp[j] = min(dp[j],dp[j & (~w[i])]+1);
        }
    if(dp[all] >= 0x3f3f3f3f)
        cout << -1 << endl;
    else
        cout << dp[all] << endl;
    return 0;
}

IDA*+步调函数

太晚了题解明早更

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 110, M = 1 << 20;

vector<int> col[30];
int log2[M];
int n, m, k;

//返回最右边1的位置,比如说:10100返回4(100)
int lowbit(int x)
{
    return x & -x;
}

//步调函数:返回至少需要再走几步
int h(int state)
{
    int res = 0;
    for(int i = (1 << m) - 1 - state; i; i -= lowbit(i))
    {
        int c = log2[lowbit(i)];
        res ++ ;
        for(auto row : col[c])  i &= ~row;
    }
    return res;
}

bool dfs(int depth, int state)
{
    if(depth == 0 || h(state) > depth) return state == (1 << m) - 1;
    
    //优先选择方案数最少的
    int t = -1;
    //减去当前状态就可以得知缺少哪一种糖果,对剩下的糖果取方案数最少的
    for(int i = (1 << m) - 1 - state; i; i -= lowbit(i))
    {
        int c = log2[lowbit(i)];
        if( t == -1 || col[c].size() < col[t].size())
            t = c;
    }
    
    for(int row : col[t])
        if(dfs(depth-1,state | row))
            return true;
    return false;
}

int main()
{
    cin >> n >> m >> k;
    for(int i = 0; i < m; ++i) log2[1 << i] = i;  //log2可以找出每个1的列数
    for(int i = 0; i < n; ++i)
    {
        int state = 0;
        for(int j = 0; j < k; ++j)
        {
            int c;
            cin >> c;
            state |= 1 << c - 1;
        }
        
        for(int j = 0; j < m; ++j)
            if(state >> j & 1)
                col[j].push_back(state);
    }
    
    //去掉重复的行
    for(int i = 0; i < m; ++i)
    {
        sort(col[i].begin(),col[i].end());
        col[i].erase(unique(col[i].begin(),col[i].end()),col[i].end());
    }
    
    int depth = 0;
    while(depth <= m && !dfs(depth,0)) depth++;
    if(depth > m)  depth = -1;
    cout << depth << endl;
    return 0;
}
这篇关于【递归/dp】Acwing1243.糖果(IDA*+步调函数/状态压缩+01背包)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!