数据结构与算法-动态规划3-状态压缩dp,数位dp

6:状态压缩dp:


通过集合的形式去表达一个状态,实现将指数级的问题转化为更方便处理的形式

例题:


求把 N×MN×M 的棋盘分割成若干个 1×21×2 的长方形,有多少种方案。

例如当 N=2,M=4N=2,M=4 时,共有 55 种方案。当 N=2,M=3N=2,M=3 时,共有 33 种方案。

如下图所示:

输入格式

输入包含多组测试用例。

每组测试用例占一行,包含两个整数 NN 和 MM。

当输入用例 N=0,M=0N=0,M=0 时,表示输入终止,且该用例无需处理。

输出格式

每个测试用例输出一个结果,每个结果占一行。

代码:

#include

using namespace std;

const long long N = 12;

long long f[N][1 << N];

bool st[1 << N];

unordered_map> e;

long long n, m;

signed main() {

    while (scanf("%lld%lld", &n, &m) != -1) {

        if (n == 0 && m == 0) break;

        memset(st,0,sizeof st);

        e.clear();

        for (long long i = 0; i < (1 << n); i++) {

            long long cnt = 0;

            bool flag = 1;

            for (long long j = 0; j < n; j++) {

                if ((1 << j) & i) {

                    if (cnt & 1) {

                        flag = 0;

                        break;

                    }

                    cnt = 0;

                } else cnt++;

            }

            if (cnt & 1) flag = 0;

            st[i] = flag;

        }

        

        for (long long i = 0; i < (1 << n); i++) {

            for (long long j = 0; j < (1 << n); j++) {

                if (st[i | j] && (i & j) == 0) {

                    e[i].push_back(j);

                }

            }

        }

        memset(f, 0, sizeof f);

        f[0][0] = 1;

        // 动态规划过程

        for (long long i = 1; i <= m ; i++) {

            for (long long j = 0; j < (1 << n); j++) {

                for (long long k : e[j]) {

                    f[i][j] += f[i - 1][k];

                }

            }

        }

        cout << f[m][0] << endl;

    }

    return 0;

}    

例题2:


给定一张 n 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。

Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数 n。

接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i−1 到 j−1 的距离(记为 a[i−1,j−1])。

对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z]。

输出格式

输出一个整数,表示最短 Hamilton 路径的长度。

f[i][j]表示状态为i且停留在j上的最短路径

代码:

#include

using namespace std;

const int N=21;

int g[N][N];

int f[1<

int n;

int main(){

    cin>>n;

    for(int i=0;i

        for(int j=0;j

            cin>>g[i][j];

    memset(f,0x3f,sizeof f);

    f[1][0]=0;

    int m=1<

    for(int i=0;i

        for(int j=0;j

            if((1<

                for(int k=0;k

                    if(k!=j&&(1<

                        f[i][j]=min(f[i][j],f[i-(1<

                    }

                }

            }

        }

    }

    cout<

    return 0;

}

7:数位dp

有一定的模版,本质就是求在某个范围内某个数字出现的次数

例题

题目描述

给定两个正整数 a 和 b,求在 [a,b] 中的所有整数中,每个数码(digit)各出现了多少次。

输入格式

仅包含一行两个整数 a,b,含义如上所述。

输出格式

包含一行十个整数,分别表示 0∼9 在 [a,b] 中出现了多少次。

代码:

#include using namespace std;

// 定义 a 和 b 为 long long 类型long long a, b;long long ten[20], f[20];

// 计算从 0 到 x 中数码 cnt 出现的次数long long get(long long x, int cnt) {

    long long num[20] = {0};

    int len = 0;

    long long res = 0;

    // 将 x 的每一位存储到 num 数组中

    while (x) {

        num[++len] = x % 10;

        x = x / 10;

    }

    // 从高位到低位处理每一位

    for (int i = len; i >= 1; i--) {

        res += f[i - 1] * num[i];

        if (cnt < num[i]) {

            res += ten[i - 1];

        }

        long long num2 = 0;

        if (cnt == num[i]) {

            for (int j = i - 1; j >= 1; j--) {

                num2 = num2 * 10 + num[j];

            }

            res += num2 + 1;

        }

        if (cnt == 0) {

            res -= ten[i - 1];

        }

    }

    return res;

}

int main() {

    // 读取输入的 a 和 b

    scanf("%lld %lld", &a, &b);

    // 初始化 ten 数组和 f 数组

    ten[0] = 1;

    for (int i = 1; i <= 15; i++) {

        f[i] = f[i - 1] * 10 + ten[i - 1];

        ten[i] = 10 * ten[i - 1];

    }

    // 计算并输出每个数码在 [a, b] 区间内出现的次数

    for (int i = 0; i < 10; i++)

        cout << get(b, i) - get(a - 1, i) << ' ';

    return 0;

}    

你可能感兴趣的:(算法学习,#,动态规划,动态规划,算法)