最近才看书,看到状态压缩。对于状态压缩DP,其实就是集合上的DP。
这需要我们了解一些位运算:
集合{0,1,2,3,....,n-1}的子集可以用下面的方法编码成整数
像这样,一些集合运算就可以用如下的方法来操作:
1.空集....................0
2.只含有第i个元素的集合{i}................1 << i
3.含有全部n个元素的集合{0,1,2,3,....,n - 1}.............(1 << n) - 1
4.判断第i个元素是否属于集合S.................................if(S >> i & 1)
5.向集合中加入第i个元素S ∪ {i}...............................S | 1 << i
6.从集合中除去第i个元素S \ {i}..................................S & ~(1 << i)
7.集合S和T的并集S∪T...............................................S | T
8.集合S和T的交集S∩T................................................S & T
而题目的意思是:一个人在m个城市的国家旅行,他有n张车票,这个国家有p条路,一条路连接两个城市,他要从a城市到b城市,从一个城市到另一个城市所需要的时间是路的长度除以车票的面值,面值是多少表示可以有多少匹马来拉,求最短的时间。
这题,不能直接用最短路径的算法来求解,有车票的限制。我们将在第i个城市看作一个状态,剩下的车票为一个集合S,从这个城市出发,使用一张车票到达城市j,现在他在第j个城市,状态为S \ {i},花费的时间就是路的长度除以车票的面值。
下面的是 AC的代码:
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const double INF = 10000000000.00; int n, m, p, a, b; int ticket[10]; int map[32][32]; double dp[1 << 10][32]; double min(double a, double b) //取最小值 { return a > b ? b : a; } void solve() { int i, j; for(i = 0; i < 1 << n; i++) //初始化dp数组 { for(j = 0; j < 32; j++) dp[i][j] = INF; } dp[(1 << n) - 1][a - 1] = 0; double res = INF; for(int S = (1 << n) - 1; S >= 0; S--) //集合S { res = min(res, dp[S][b - 1]); for(int v = 0; v < m; v++) { for(i = 0; i < n; i++) //枚举车票 { if(S >> i & 1) //判断第i个车票是否属于集合S { for(int u = 0; u < m; u++) { if(map[v][u] >= 0) //城市v到u有路,使用第i个车票从v到u { dp[S & ~(1 << i)][u] = min(dp[S & ~(1 << i)][u], dp[S][v] + double(map[v][u]) / ticket[i]); } } } } } } if(res == INF) printf("Impossible\n"); else printf("%.3lf\n", res); } int main() { // freopen("data.txt", "r", stdin); int i, x, y, z; while(cin >> n >> m >> p >> a >> b) { memset(map, -1, sizeof(map)); if(n == 0 && m == 0 && p == 0 && a == 0 && b == 0) break; for(i = 0; i < n; i++) //输入数据 cin >> ticket[i]; for(i = 0; i < p; i++) { cin >> x >> y >> z; map[x - 1][y - 1] = z; map[y - 1][x - 1] = z; } solve(); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。