Description
Input
Output
Sample Input
3 4 3 1 4 3 1 2 1 2 10 2 3 30 3 4 20 2 4 4 2 1 3 1 2 3 3 1 3 3 4 1 2 4 2 5 2 4 3 4 1 5 5 1 2 10 2 3 10 3 4 10 1 2 0 1 2 1 8 5 10 1 5 2 7 1 8 4 5 6 3 1 2 5 2 3 4 3 4 7 4 5 3 1 3 25 2 4 23 3 5 22 1 4 45 2 5 51 1 5 99 0 0 0 0 0
Sample Output
30.000 3.667 Impossible Impossible 2.856
Hint
30.0 3.66667 Impossible Impossible 2.85595
题意:给出图论中的n,m,p,st,en及u,v,w。n表示有n组马的个数Pn,m表示大城市个数,p有多少条路,st开始城市,en终点城市;然后再给出马的个数Pn,w/Pn=花费的时间。然后求出能有路径并且花费时间与路径都少的。
思路:以前还没专攻过状压,所以这道题现在才写。前两天专攻了一下状压DP,比较掌握了状压的本质才开始做了这题。刚开始用邻接表T了,检查好久感觉也没有错啊,唉……然后觉得可能是邻接表的句子太多,如果p很大的话,会多出一些时间导致的T 吧,所以就改成了vector就过了……真是又受教了!
状压中dp[state][u]表示:在state状态下到达u结点所用的最小时间花费。state微观来说表示的是二进制状态下0与1不同的位置情况。就像捅死组合那样,0与1出现的位置不同,则组合情况不同,就可以用来表示一个状态了。
状压DP:其本质就是把数想像为二进制的状态,因为每个数其二进制中0和1的位置是不一样的,所以出现0和1的情况拿来做讨论,就表示一个状态。比如出现1的位置为开灯,0的位置为关灯:则长度为4的二进制数数字6二进制为:0110,则0110第二第三个位置为1表示开灯,第一第四个位置表示关灯,则0110表示一个状态,即数字四表示一个状态;又如数字2:0010也表示一个状态(第三个位置表示开灯)。 根据这些像排列组合的二进制0与1的不同位置来做的动态规划就是状态压缩动态规划。
在状压DP中经常用的二进制计算符号为&与|。
经常用的计算式子为:i&(1<<j)表示判断1右移 j 位后与i中相应的位置取&,如果结果为真,则说明i中这个位置为1,否则为0;
(i|(1<<j))!=i表示判断1右移j位后与i中相应的位置取|,如果结果为真,则说明为i中的这个位置为0,否则为1。
#include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <list> #include <queue> #include <string> #include <cstring> #include <map> #define PI acos(-1.0) #define mem(a,b) memset(a,b,sizeof(a)) #define sca(a) scanf("%d",&a) #define pri(a) printf("%d\n",a) #define MM 500002 #define MN 1002 #define INF 168430090 using namespace std; typedef long long ll; double dp[1<<9][33],px[9]; int main() { int n,m,p,st,en,i,j,k,r,u,v; while(scanf("%d%d%d%d%d",&n,&m,&p,&st,&en)&&(n||m||p||st||en)) { double w,ans=INF; vector<pair<int,int> >q[33]; for(i=0;i<n;i++) scanf("%lf",&px[i]); for(i=0;i<=(1<<n);i++) for(j=0;j<=m;j++) dp[i][j]=INF; for(i=0;i<p;i++) { scanf("%d%d%lf",&u,&v,&w); q[u].push_back(make_pair(v,w)); //刚开始用邻接表T了,因为p可能很大,I觉得邻接表入边句子太多所以T了 q[v].push_back(make_pair(u,w)); } dp[0][st]=0; for(i=0;i<(1<<n);i++) { for(j=1;j<=m;j++) if(dp[i][j]!=INF) //如果为INF,那么下面取小的时候也没意义,所以把这个省去了 for(k=0;k<q[j].size();k++) { v=q[j][k].first; w=q[j][k].second; for(r=0;r<n;r++) if(!(i&(1<<r))) //右移r位后i中相应位置为0的情况,下面的i|(1<<r)则是把这个位置的0变成1 dp[i|(1<<r)][v]=min(dp[i|(1<<r)][v],dp[i][j]+w/px[r]); } ans=min(ans,dp[i][en]); } if(ans==INF) puts("Impossible"); else printf("%.3f\n",ans); } return 0; }