结合poj 2686,入门状态压缩dp

poj 2686

题意:有m个城市,p条道路将他们连接起来,每经过一条道路需要缴纳车票,车票上印有马的匹数,因此通过一条道路等于道路距离/马的匹数,一共有n张票。问从a点到b点最少需要多少时间

假如不要车票的话,这道题无疑是最短路的算法。但是每条路均可以使用车票,假如将所有可行的方法保存下来,用贪心的思想将方法中经过的道路从长到短排序,票最多的匹配最长的道路,试试就逝世,再往下想会发现这程序写起来其实挺复杂冗长的。

状态压缩dp,一般是将状态转化为集合,利用集合来dp,在数据量较小的情况下,往往使用二进制来代替状态会很方便。

那么就可以将没车票当成数字1,有车票当成数字0(这样表示在本题中更方便),然后遍历所有车票状态(车站有30个,这样无法完全遍历,超时),dp【s】【v】,s表示车票状态,v表示现在在哪里,数组表示走到点v并且拥有s的车票的最短用时。起初将所有点设为inf(最大),然后遍历所有点,假如某个点的dp<inf,则以该点为中心更新其旁边的所有点

其实这道题挺裸的,会一些dp的我估计打打就能出来了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=1<<30;
const int maxn=1<<10; // city 太多,用ticket 可以 
int ticket[10],dis[40][40];  
double dp[maxn][31];
int main( ){
	int n,m,p,a,b;
	while(cin>>n>>m>>p>>a>>b){
		if(n==0&&m==0&&p==0&&a==0&&b==0) break;
		memset(dis,0,sizeof(dis));
		memset(ticket,0,sizeof(ticket));
		for(int i=0;i<n;i++) scanf("%d",&ticket[i]);
		for(int i=1;i<=p;i++){
			int fro,to,val;
			scanf("%d %d %d",&fro,&to,&val);
			dis[fro][to]=val;
			dis[to][fro]=val;
		}
		for(int i=0;i<(1<<n);i++){
			fill(dp[i]+1,dp[i]+m+1,INF);
		}
		dp[0][a]=0;
		double res=INF;
		for(int s=0;s<(1<<n);s++){ // 1: 无票,0:有票 
			for(int i=1;i<=m;i++){
					for(int j=1;j<=m;j++){ 
						if(dis[i][j]>0){				
							for(int k=0;k<n;k++){
								if(!( s>>k & 1)) dp[s|(1<<k)][j]=min(dp[s|(1<<k)][j],dp[s][i]+(double)dis[i][j]/ticket[k]);
							}
						}
					}
			}
		}
		for(int i=1;i<(1<<n);i++){
			res=min(res,dp[i][b]);
		}
		if(res!=INF) printf("%.3lf\n",res); //double 是 lf 
		else printf("Impossible\n");
	} 
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/weixin_43191865/article/details/88235731
今日推荐