状压DP笔记

文章介绍了旅行商问题的解决方法,通过二进制表示集合并优化计算过程,给出了AC代码实现。同时,提出了一个与电梯载重相关的资源分配问题,同样使用动态规划求解最少电梯行程。最后,提到了一个排列问题,统计数字串的不同排列能被特定数整除的个数。
摘要由CSDN通过智能技术生成

1.旅行商问题 

核心思路

用二进制表示一个集合S,如101101 表{0,2,3,5},即第i位为1则i在集合中。

判断j集合S 第v位 是不是1: (S >> v)&1 == 1 则是1

2^{n+1}-1 = bit(1111..n...1)。

在S集合的第i位插入 S I (1 << i)或 S + (1 << i)。

dp_{i,j}表示i集合,最后停留在j点。

u表示出发点,v表要到达的点,则dp_{s+(1 << v),v} = min(dp_{s+(1 << v),v},dp_{s,u}+w_{u,v})

AC代码

#include<bits/stdc++.h>
using namespace std;
int dp[100010][110];
int w[100][100];
int n;


int main(){
    cin>>n;
    for(int i = 0;i < n*(n+1);i++){
   	   int u,v;
   	   cin>>u>>v;
   	   cin>>w[u][v];
    }
    for(int i = 0;i <= 100000;i++){
    	for(int j = 0;j <= 99;j++){
    		dp[i][j] = 1e9;
		}
	}
    dp[1][0] = 0;
    int ALL = (1 <<(n+1))-1;
    for(int S = 1;S < ALL;S++){
    	for(int u = 0;u <= n;u++){
    		for(int v = 1;v <= n;v++){
    			if(((S >> v) & 1) == 0){
    				dp[S|(1 << v)][v] = min(dp[S+(1 << v)][v],dp[S][u]+w[u][v]);
				}
			}
		}
	}
	int ret = 1e9;
	for(int u = 1;u <= n;u++){
		ret = min(ret,dp[ALL][u]+w[u][0]);
	}
	cout<<ret;
	return 0;
} 

B. 例题 2 Cows in a Skyscraper

题目描述

A little known fact about Bessie and friends is that they love stair climbing races. A better known fact is that cows really don't like going down stairs. So after the cows finish racing to the top of their favorite skyscraper, they had a problem. Refusing to climb back down using the stairs, the cows are forced to use the elevator in order to get back to the ground floor.

The elevator has a maximum weight capacity of W (1 <= W <= 100,000,000) pounds and cow i weighs C_i (1 <= C_i <= W) pounds. Please help Bessie figure out how to get all the N (1 <= N <= 18) of the cows to the ground floor using the least number of elevator rides. The sum of the weights of the cows on each elevator ride must be no larger than W.

给出n个物品,体积为w[i],现把其分成若干组,要求每组总体积<=W,问最小分组。(n<=18)

输入格式

* Line 1: N and W separated by a space.

* Lines 2..1+N: Line i+1 contains the integer C_i, giving the weight of one of the cows.

输出格式

* A single integer, R, indicating the minimum number of elevator rides needed.

one of the R trips down the elevator.

输入输出样例

输入 #1复制

4 10 
5 
6 
3 
7 

输出 #1复制

3 

说明/提示

There are four cows weighing 5, 6, 3, and 7 pounds. The elevator has a maximum weight capacity of 10 pounds.

We can put the cow weighing 3 on the same elevator as any other cow but the other three cows are too heavy to be combined. For the solution above, elevator ride 1 involves cow #1 and #3, elevator ride 2 involves cow #2, and elevator ride 3 involves cow #4. Several other solutions are possible for this input.

核心思路

dp_s = min(dp_s,dp_{s-s'}+1) s'为S的子集。

如何判断是不是 S | S' == S 则是。

原因:多出来到就不是了。

优化:从集合S开始,倒序枚举 用 T  &= S强行枚举。

时间复杂度:O(3^{n})

AC代码

#include<bits/stdc++.h>
using namespace std;
int dp[1000010],sum[1000010],c[100010];
int w;
int n;
int main(){
    cin>>n>>w;;
    for(int i = 0;i <= 1000000;i++){
    		dp[i] = 1e9;
	}
    for(int i = 0;i < n;i++){
    	cin>>c[i];
	}
    int ALL = (1 << n)-1;
    for(int S = 1;S <= ALL;S++){
    	for(int i = 0;i < n;i++){
    		if((S >> i)& 1) sum[S] += c[i];
		}
	}
    dp[0] = 0;
    for(int S = 1;S <= ALL;S++){
    	for(int T = S;T >= 1;T--){
    		T &= S;
    		if( sum[T] <= w){
    			dp[S] = min(dp[S],dp[S-T]+1);
			}
		}
	}
	cout<<dp[ALL];
	return 0;
} 

C. 例题 Corn Fields

link

题目描述

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

农场主 JohnJohn 新买了一块长方形的新牧场,这块牧场被划分成 �M 行 �N 列 (1≤�≤12;1≤�≤12)(1≤M≤12;1≤N≤12),每一格都是一块正方形的土地。 JohnJohn 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 JohnJohn 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

JohnJohn 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

输入格式

第一行:两个整数 �M 和 �N,用空格隔开。

第 22 到第 �+1M+1 行:每行包含 �N 个用空格隔开的整数,描述了每块土地的状态。第 �+1i+1 行描述了第 �i 行的土地,所有整数均为 00 或 11 ,是 11 的话,表示这块土地足够肥沃,00 则表示这块土地不适合种草。

输出格式

一个整数,即牧场分配总方案数除以 100,000,000100,000,000 的余数。

输入输出样例

输入 #1复制

2 3
1 1 1
0 1 0

输出 #1复制

9

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 12+5,maxs = (1 << 12)+5;
int n,m;
int a[maxn][maxn];
int dp[maxn][maxs];
bool check(int i ,int S){
	int last = 100;
	for(int j = 0;j < m;j++){
		if((S >> j)& 1){
			if(a[i][j+1] == 0)return 0;
			if(j == last+1)return 0;
			last = j;
		}
	}
	return 1;
}
const int mod = 1e8;
int main(){
    cin>>n>>m;
    for(int i = 1;i <= n;i++){
    	for(int j = 1;j <= m;j++){
    		cin>>a[i][j];
		}
	}
	int ALL = (1 << m)-1;
	dp[0][0]  = 1;
	for(int i = 0;i < n;i++){
		for(int S = 0;S <= ALL;S++){
			if(dp[i][S] > 0){
				int T = S ^ ALL;
				for(int TT = T;TT >= 0;TT--){
					TT &= T;
					if(check(i+1,TT)) dp[i+1][TT] = (dp[i][S]+dp[i+1][TT])%mod;
				}
			}
		}
	}
	int ret = 0;
	for(int S = 0;S  <= ALL;S++)ret = (ret+dp[n][S])%mod;
	cout<< ret;
	return 0;
} 

计算可能的方法类似摆花,可以看我之前的题解。

D. 排列

时间限制:1000 ms内存限制:256 MB类型:传统评测:文本比较上传者: sysulby

提交提交记录

题目描述

原题来自:SCOI 2007

给一个数字串  和正整数 ,统计  有多少种不同的排列能被  整除(可以有前导 0)。

例如 123434 有  种排列能被  整除,其中末位为 2 的有  种,末位为 4 的有  种。

输入格式

第一行有一个整数T,表示测试数据的个数。

每组数据一行,有一个字符串  与一个整数 ,保证  中只包含数字字符。

输出格式

每组数据输出一行,一个整数,表示能被  整除的排列的个数。

样例
样例输入复制
7
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29
样例输出复制
1
3
3628800
90
3
6
1398

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 15+5,maxs = (1 << 10)+5;
string s;
long long d;
long long dp[maxs][1001];
long long cnt[100],fac[100]; 
int main(){
    int T;
    cin>>T;
    while(T--){
    	cin>>s>>d;
    	int n = s.size();
    	int ALL = (1 << n)-1;
    	memset(dp,0,sizeof(dp));
    	dp[0][0] = 1;
    	for(int i = 0;i <= 9;i++)cnt[i] = 0,fac[i] = 1;
    	for(int i = 0;i < n;i++){
    		cnt[s[i]-'0']++;
		}
		for(int i = 0;i <= 9;i++){
    	    for(int j = 1;j <= cnt[i];j++){
    	    	fac[i] *= j;
			}
		}
    	for(int S = 0;S < ALL;S++){
    		for(int r = 0;r < d;r++){
    			if(dp[S][r]){
    				for(int i = 0;i < n;i++){
    					if(((S >> i)&1) == 0){
    						dp[S+(1 << i)][(r*10+s[i]-'0')%d] += dp[S][r];
						}
					}
				}
			}
		}
		long long ans = dp[ALL][0];
		for(int i = 0;i <= 9;i++){
			ans = ans/fac[i];
		}
		cout<<ans<<endl;
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值