通过集合的形式去表达一个状态,实现将指数级的问题转化为更方便处理的形式
求把 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
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;
}
给定一张 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; } 有一定的模版,本质就是求在某个范围内某个数字出现的次数 题目描述 给定两个正整数 a 和 b,求在 [a,b] 中的所有整数中,每个数码(digit)各出现了多少次。 输入格式 仅包含一行两个整数 a,b,含义如上所述。 输出格式 包含一行十个整数,分别表示 0∼9 在 [a,b] 中出现了多少次。 #include // 定义 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; } 7:数位dp
例题
代码: