状态压缩DP和普通DP唯一的区别就是它所枚举的对象不再是一个整数,而是一个集合,解决的策略就是利用二进制将这个集合压缩成一个整数。
对于该题,dp[s][v]表示:s表示在该城市剩下的车票集合,v表示在城市v,dp表示在该状态的最小话费。
影响决策的因素有一下几个:
1.一共使用哪几个车票(包括数量和种类)
2.当前从哪个城市到哪个城市
3.使用哪个车票完成两个城市的转移
所以一共需要枚举4个量,用4重循环完成状态转移。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int max_n = 15; const int max_m = 35; const int INF = 1000000; int n,m,p,a,b,t[max_n],d[max_m][max_m]; double dp[1 << max_n][max_m]; void solve() { for(int i=0;i<1<<n;i++){ fill(dp[i],dp[i]+m,INF); } dp[(1<<n) - 1][a-1] = 0; double res = INF;//用二进制位保存集合,1表示有票,0表示没票 for(int s=(1<<n)-1;s>=0;s--){ //枚举使用的车票个数, 不一定车票都用才好 res = min(res,dp[s][b-1]); for(int v=0;v<m;v++){ for(int i=0;i<n;i++){ //表示使用第几张车票 if(s>>i&1){ for(int u=0;u<m;u++){ if(d[v][u]>=0){ dp[s & ~(1<<i)][u] = min(dp[s & ~(1<<i)][u],dp[s][v]+(double)d[v][u]/t[i]); } } } } } } if(res==INF) printf("Impossible\n"); else printf("%.3f\n",res); } int main() { while(~scanf("%d%d%d%d%d",&n,&m,&p,&a,&b)){ if(!n&&!m&&!p&&!a&&!b) return 0; memset(d,-1,sizeof(d)); for(int i=0;i<n;i++) scanf("%d",&t[i]); for(int i=0;i<p;i++) { int q,w,e; scanf("%d%d%d",&q,&w,&e); q--; w--; d[q][w] = d[w][q] = e; } solve(); } return 0; }
时间: 2024-11-09 09:33:32