poj -2686(状压dp)

题面太长了就不放了。
题目给你 n,m,p,a,b 5个数,分别代表票数,点数,边数,起点和终点,每张票有一个权值,每条边有一个权值,每走一条边消耗一张票,代价为边权值/票权值,问是否能从起点走到终点,以及最小的总代价是多少。
刚开始写的时候莫名其妙给自己证明了一下最短路即为最优路,然后把路经边的权值排序后再和票权值匹配,于是就开心地过不了样例,即5/3 + 2/1 < 3/3 + 1/3。dp真的是一生之敌。

#include<cstdio> 
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<ctime> 
#define outtime() cerr<<"User Time = "<<(double)clock()/CLOCKS_PER_SEC<<endl
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define bug printf("bug**")
#define coutlf(x) cout << fixed << setprecision(x)
#define uncoutlf  cout.unsetf(ios::fixed)
#define S_IT set<Node>::iterator
#define endl "\n"
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)
using namespace std;
int gcd(int a,int b) { return b?gcd(b,a%b):a;}
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pII;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
int com(double a, double b){
	if(a - b > eps) return 1;
	if(b - a > eps) return -1;
	return 0;
}
int n ,m, p, a, b, u, v, w;
int dis[40][40];
double dp[512][40];
//前一位表示状态,后一位表示到某个点,存a在某个状态下到某个点的最短距离
double t[10];
int cmp(double a, double b){
	return a - b > eps;	
}
void init(){
	memset(dp, 127, sizeof(dp));//搞了半天发现double数组memset 0x3f会取一个很小的值
	memset(dis, -1, sizeof(dis));
}
void solve(){
	while(~scanf("%d %d %d %d %d", &n, &m, &p, &a, &b)){
		if(!(a | b | n | m | p)) break;
		init();//初始化边和dp距离
		for(int i = 0; i < n; ++i) scanf("%lf", &t[i]);
		for(int i = 1; i <= p; ++i){
			scanf("%d %d %d", &u, &v, &w);
			dis[u][v] = dis[v][u] = w;
		}
		dp[0][a] = 0;
		for(int s = 0; s < 1 << n; ++s){//状态表示不会超过2^n
			for(int i = 0; i < n; ++i){ 
				if(s >> i & 1){//当前状态下要用第i张票
					for(int u = 1; u <= m; ++u){
						for(int v = 1; v <= m; ++v){
							if(u != v && dis[u][v] >= 0){//两点之间有边
								dp[s][v] = min(dp[s][v], dp[s & ~(1 << i)][u] + dis[u][v] / t[i]);
								//s&~(1<<i)为不用当前这张票的情况
							}
						}
					}
				}
			}
		}
		double ans = inf;
		for(int i = 0; i < 1 << n; ++i){
			ans = min(ans, dp[i][b]);
		}
		if(ans == inf) puts("Impossible");
		else printf("%.3f\n", ans);
	}
}

int main(){
	solve();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值