城市旅游购物交通咨询模拟(最小环floyd法/dp)
题目摘要
城市旅游购物交通咨询模拟
【问题描述】
沈阳城内有若干旅游观光景点和商业区。游客主要以公交车为交通工具出游。假设往返于每个景点和商业区的公交线路不少于6路。旅客希望中转次数最少、时间最短、费用最省。
【设计要求】
设计城市交通咨询模拟程序。
(1)采用图结构、集合等数据结构。
(2)可以随机、文件及人工输入数据。
(3)可以完成旅游、购物一日游的最佳线路。
(4)可以统计数据并满足必要的约束条件。
(5)可以查询和更新数据。
(6)其它完善性或扩展性功能。
思路
此题和URAL - 1004的题目类似,可以按照两种方法,由于是普通的课设,我就按照的是floyd 法来写的,不过需要注意保存路径,以及求最小环的具体算法部分。
Floyd法
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<bits/stdc++.h>
using namespace std;
const int INF = 1 << 28;
const int maxn = 100 + 10;
/**/
int n, m;
int ans, num,sum[2],ans1,num1,sum1[2],num2,sum2;
int g[maxn][maxn], dis[maxn][maxn],cost[maxn][maxn],g1[maxn][maxn];
int path[maxn],path1[maxn],pre[maxn][maxn],pre1[maxn][maxn],pre2[maxn][maxn];
int path2[maxn];
void init(int flag,int t) {
FILE *fp;
if(flag) {
if(t==1) fp=fopen("input.txt","r");
if(t==2) fp=fopen("rand.txt","r");
}
n=m=1;
printf("请输入城市景点数和交通线路数量:\n");
if(!flag) {
do {
if(n<=0||m<=0) printf("请重新输入正确的值:\n");
scanf("%d %d", &n,&m);
} while(n<=0||m<=0);
} else fscanf(fp,"%d %d", &n,&m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (i == j) g1[i][j] = g[i][j] = dis[i][j] = cost[i][j] = 0;
else g1[i][j] = g[i][j] = dis[i][j] = cost[i][j] = INF;
pre[i][j] = pre1[i][j] =pre2[i][j]= i;
}
printf("请输入交通线路(线路预估时间和目的地景点门票价格):\n");
for (int i = 0; i < m; i++) {
int u, v, w, c;
if(!flag) scanf("%d %d %d %d", &u, &v, &w, &c);
else fscanf(fp,"%d %d %d %d", &u, &v, &w, &c);
if (dis[u][v] > w) {
g[u][v] = g[v][u] = dis[u][v] = dis[v][u] = w;
}
if (cost[u][v] > c) {
g1[u][v] = g1[v][u] = cost[u][v] = cost[v][u] = c;
}
}
printf("读取成功!\n");
if(flag) fclose(fp);
}
void randd() {
FILE *fp;
fp=fopen("rand.txt","w");
int a=5,b=20;
int c=(rand() % (b-a+1)) + a;
a=c,b=c*(c-1);
int d=(rand() % (b-a+1)) + a;
fprintf(fp,"%d %d\n",c,d);
for(int i = 0; i<d; i++)
fprintf(fp,"%d %d %d %d\n",(rand() % c) + 1,(rand() % c) + 1,(rand() % (300-10+1)) + 10,(rand() % (300-10+1)) + 10);
fclose(fp);
init(1,2);
}
void floyd(int s,int t) {
sum2=0;
num2=0;
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (dis[i][j] > dis[i][k] + dis[k][j]) {
dis[i][j] = dis[i][k] + dis[k][j];
pre2[i][j] = pre2[k][j];
}
}
}
if(dis[s][t]<INF&&dis[s][t]) {
int p = t;
while (p != s) {
path2[num2++] = p; //记录路径
p = pre2[s][p];
}
path2[num2++] = s;
for(int i=num2-1; i>=0; i--) {
printf("%d ",path2[i]);
}
for (int i = 0; i < num2-1; i++) {
sum2+=g[path2[i]][path2[i+1]];
}
puts("");
printf("总时间为%d 中转次数为:%d\n",sum2,num2);
} else printf("抱歉,没有合适的路线。\n");
}
void floyd1() {
ans = INF;
for (int k = 1; k <= n; k++) {
for (int i = 1; i < k; i++)
for (int j = i + 1; j < k; j++) {
int tmp = dis[i][j] + g[i][k] + g[k][j]; //从k点出发,回到k点
if (tmp < ans) {
ans = tmp;
num = 0;
int p = j;
while (p != i) {
path[num++] = p; //记录路径
p = pre[i][p];
}
path[num++] = i;
path[num++] = k;
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (dis[i][j] > dis[i][k] + dis[k][j]) {
dis[i][j] = dis[i][k] + dis[k][j];
pre[i][j] = pre[k][j];
}
}
}
int p[maxn];
for (int i = 0; i < num; i++) {
p[i]=path[i];
}
sum[0]=sum[1]=0;
for (int i = 0; i < num-1; i++) {
sum[0]+=g[p[i]][p[i+1]];
sum[1]+=g1[p[i]][p[i+1]];
}
sum[0]+=g[p[num-1]][p[0]];
sum[1]+=g1[p[num-1]][p[0]];
}
void floyd2() {
ans1 = INF;
for (int k = 1; k <= n; k++) {
for (int i = 1; i < k; i++)
for (int j = i + 1; j < k; j++) {
int tmp = cost[i][j] + g1[i][k] + g1[k][j]; //从k点出发,回到k点
if (tmp < ans1) {
ans1 = tmp;
num1 = 0;
int p = j;
while (p != i) {
path1[num1++] = p; //记录路径
p = pre1[i][p];
}
path1[num1++] = i;
path1[num1++] = k;
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (cost[i][j] > cost[i][k] + cost[k][j]) {
cost[i][j] = cost[i][k] + cost[k][j];
pre1[i][j] = pre1[k][j];
}
}
}
int p[maxn];
for (int i = 0; i < num1; i++) {
p[i]=path1[i];
}
sum1[0]=sum1[1]=0;
for (int i = 0; i < num1-1; i++) {
sum1[0]+=g[p[i]][p[i+1]];
sum1[1]+=g1[p[i]][p[i+1]];
}
sum1[0]+=g[p[num1-1]][p[0]];
sum1[1]+=g1[p[num1-1]][p[0]];
}
void ConsultMenu() { /*咨询系统菜单*/
printf("\n\n");
printf("************************欢迎进入咨询系统**********************\n");
printf("* 1.咨询最省钱一日游路线 *\n");
printf("* 2.咨询最短时间一日游路线 *\n");
printf("* 3.咨询最快路线 *\n");
printf("* 0.退出 *\n");
printf("**************************************************************\n");
printf("\n\n");
}
void ManageMenu() { /*管理系统菜单*/
printf("\n\n");
printf("************************欢迎进入管理系统**********************\n");
printf("* 1.初始化线路 *\n");
printf("* 2.更新线路 *\n");
printf("* 3.删除线路 *\n");
printf("* 4.查询线路 *\n");
printf("* 5.从文件读取线路 *\n");
printf("* 6.生成随机线路(供测试) *\n");
printf("* 0.退出 *\n");
printf("**************************************************************\n");
printf("\n\n");
}
void MainMenu() { /*主菜单*/
printf("\n\n");
printf("**************欢迎使用城市旅游购物交通咨询模拟系统************\n");
printf("* 1.管理系统 *\n");
printf("* 2.咨询系统 *\n");
printf("* 0.退出 *\n");
printf("**************************************************************\n");
printf("\n\n");
}
void Change() { //更新线路
printf("请输入起始点,更改后的线路预估时间和目的地景点门票价格:\n");
int u, v, w, c;
scanf("%d%d%d%d", &u, &v, &w, &c);
g[u][v] = g[v][u] = dis[u][v] = dis[v][u] = w;
g1[u][v] = g1[v][u] = cost[u][v] = cost[v][u] = c;
printf("更新成功!\n");
}
void Del() { //删除线路
int u,v;
printf("请输入起始点:\n");
scanf("%d%d", &u, &v);
g[u][v] = g[v][u] = dis[u][v] = dis[v][u] = INF;
g1[u][v] = g1[v][u] = cost[u][v] = cost[v][u] = INF;
printf("删除成功!\n");
}
void Find() { //查询某条线路
int u,v;
printf("请输入起始点:\n");
scanf("%d%d", &u, &v);
if((g[u][v]^INF&&g[u][v])||(g[v][u]^INF&&g[v][u])) {
printf("线路信息为:\n");
printf("起始点 终止点 预估时间 价格:\n");
printf("%d %d %d %d :\n", u, v, g[u][v], g1[u][v]);
} else {
printf("Sorry, there is no such route by bus.\n");
}
}
void ManageSystem() {/*管理系统*/
int flag = 1;
while(flag) {
system("cls");
ManageMenu();
int choice=0;
printf("请选择要执行的操作:\n");
scanf("%d",&choice);
switch(choice) {
case 1:
init(0,0);
system("pause") ;
break;
case 2:
Change();
system("pause") ;
break;
case 3:
Del();
system("pause") ;
break;
case 4:
Find();
system("pause") ;
break;
case 5:
init(1,1);
system("pause") ;
break;
case 6:
randd();
system("pause") ;
break;
case 0:
flag = 0;
break;
default:
printf("请输入正确的序号。\n");
system("pause");
}
}
}
void ConsultSystem() { /*咨询系统*/
int flag = 1;
while(flag) {
system("cls");
ConsultMenu();
int choice=0;
printf("请选择要咨询的路线方式:\n");
scanf("%d",&choice);
switch(choice) {
case 1: //咨询最省钱一日游路线
floyd2();
if (ans1 >= INF) printf("No solution.\n");
else {
printf("以下是最省钱一日游路线,您可以选择最近的点开始您的旅游!\n");
for (int i = 0; i < num1; i++) printf("%d ", path1[i]);
printf(" 总时间为%d 总费用为%d 中转次数为:%d\n",sum1[0],sum1[1],num1);
}
system("pause");
break;
case 2:
floyd1();
if (ans >= INF) printf("No solution.\n");
else {
printf("以下是最省时间一日游路线,您可以选择最近的点开始您的旅游!\n");
for (int i = 0; i < num; i++) printf("%d ", path[i]);
printf(" 总时间为%d 总费用为%d 中转次数为:%d\n",sum[0],sum[1],num);
}
system("pause");
break;
case 3:
printf("请输入起点和终点:\n");
int x,y;
do {
if(x<=0||x>m||y<=0||y>n) printf("请重新输入正确的值:\n");
scanf("%d %d", &x,&y);
} while(x<=0||x>m||y<=0||y>n);
floyd(x,y);
system("pause");
break;
case 0:
flag = 0;
break;
default:
printf("请输入正确的序号。\n");
system("pause");
}
}
}
int main() {
srand((int)time(0));
int flag = 1;
while(flag) {
system("cls");
int choice;
MainMenu();
printf("请选择要进入的系统:");
scanf("%d",&choice);
switch(choice) {
case 1:
ManageSystem();
break;
case 2:
ConsultSystem();
break;
case 0:
flag = 0;
break;
default:
printf("请输入正确的序号。\n");
system("pause");
}
}
return 0;
}
/*游客主要以公交车为交通工具出游。
假设往返于每个景点和商业区的公交线路不少于6路。
旅客希望中转次数最少、时间最短、费用最省。*/
状压dp
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#define DEBUG printf("Passing [%s] in Line %d\n" , __FUNCTION__ , __LINE__) ;
const int Percent[4] = {6 , 4 , 45 , 55} ;
const int MAX_N = 20 + 5 , MAX_S = (1 << 20) + 10 , ADAY = 12 * 60 , INF = 0x3f3f3f3f ;
struct DATA {
int val , dis , times ;
friend bool operator <(DATA a , DATA b) {return a.val < b.val ;}
friend bool operator >(DATA a , DATA b) {return a.val > b.val ;}
friend DATA operator +(DATA a , DATA b) {return (DATA){a.val + b.val , a.dis + b.dis , a.times + b.times} ;}
}dis[MAX_N][MAX_N] , f[MAX_S][MAX_N] ;
//vertex 0 means home
int T , n , m , cost[MAX_N] , g[MAX_S][MAX_N] ;
void init() {
for (int i = 0 ; i <= n ; ++i)
for (int j = 0 ; j <= n ; ++j) dis[i][j] = (DATA){INF , 0 , 0} ;
}
void print(int S , int a) {
if (!S) return ;
print(S - (1 << a) , g[S][a]) ;
printf("%d " , a) ;
}
int main() {
scanf("%d" , &T) ;
for (; T-- ;) {
scanf("%d %d" , &n , &m) ;
for (int i = 1 ; i <= n ; ++i) scanf("%d" , &cost[i]) ;
init() ;
//Calculate the best path
for (int i = 0 ; i < m ; ++i) {
int x , y , w ;
scanf("%d %d %d" , &x , &y , &w) ;
dis[x][y] = dis[y][x] = (DATA){w * Percent[0] + 1 * Percent[1] , w , 1} ;
}
for (int k = 0 ; k <= n ; ++k)
for (int i = 0 ; i <= n ; ++i)
for (int j = 0 ; j <= n ; ++j)
dis[i][j] = std::min(dis[i][k] + dis[k][j] , dis[i][j]) ;
//Calculate all of the solutions in the best situation
int siz = 1 << (n + 1) ;
for (int S = 0 ; S < 2 ; ++S)
for (int i = 0 ; i <= n ; ++i) f[S][i] = (DATA){-1 , 0 , 0} ;
f[1][0] = (DATA){0 , 0 , 0} ;
for (int S = 2 ; S < siz ; ++S)
for (int i = 0 ; i <= n ; ++i) {
f[S][i] = (DATA){-1 , 0 , 0} ;
if (!(S & (1 << i))) continue ;
int preS = S - (1 << i) ;
if (!preS) continue ;
for (int j = 0 ; j <= n ; ++j) {
if (f[preS][j].val == -1) continue ;
DATA tmp = f[preS][j] + dis[i][j] ;
if (f[S][i] > tmp || f[S][i].val == -1) f[S][i] = tmp , g[S][i] = j ;
}
}
//Assess
int BSet = 0 , BEnd = -1 , BValue = -1 ;
for (int S = 2 ; S < siz ; ++S) {
int BestEnd = -1 ; DATA BestVal = (DATA){-1 , 0 , 0} ;
for (int i = 0 ; i <= n ; ++i) {
if (f[S][i].val == -1) continue ;
DATA tmp = f[S][i] + dis[i][0] ;
if (tmp.dis <= ADAY && (BestVal > tmp || BestVal.val == -1)) BestVal = tmp , BestEnd = i ;
}
if (BestVal.val == -1) continue ;
int TotalCost = 0 , Count = 0 ;
for (int i = 1 ; i <= n ; ++i) if (S & (1 << i)) TotalCost += cost[i] , ++Count ;
int AssessVal = (TotalCost * Percent[2] + BestVal.val * Percent[3]) / Count ;
if (BValue > AssessVal || BValue == -1) BSet = S , BEnd = BestEnd ;
}
print(BSet , BEnd) ; printf("0\n") ;
}
return 0 ;
}
/*
部分变量解释
w[i]的单位为分钟
ADAY 表示一天可游玩的时间,依据w[i]的单位而定,此处为 12 * 60
可以根据需求而修改
其余变量下面会进行详细解释
---
输入格式
T
n m
cost[0 ... n]
u[0...m] v[0...m] w[0...m]
其中
T为数据组数
n为地点 m为交通路线
cost[i]为参观每个地点所需的费用
u[i] v[i] w[i]表示存在一条连接u[i]与v[i]且消耗时间为w[i]的交通路线(双向)
---
思路
**第一部分**
对每条路径而言,存在 w 的时间消耗 以及 1 的中转消耗
对路径进行加权平均计算价值
价值计算方法为
(w * Percent[0] + 1 * Percent[1]) / (Percent[0] + Percent[1])
为了简便运算 在编写过程中将分母除去,仅以分子作为价值
对加权平均后的图跑 Floyd 可得到任意两点 时间和中转最优的方案
如果需要保存路径 可在 Floyd 处添加记录
**第二部分**
设 f[S][i] 表示观光 S 集合内的地点且最后访问的地点为i的最优方案
利用dp可轻易计算出方案,其中 g[S][i] 用于保存dp是从哪个状态转移过来的,便于输出方案
**第三部分**
对于每个观光方案 S ,都找出最优的最后访问节点 BestEnd
即先确定当前方案 S 的最优走法 BestVal ,且保证这个走法的时间是严格限制在ADAY范围内的
(保证一日游的需求,如果需要添加地点游玩时间,可在ADAY的判断条件处稍作修改)
接着对于该方案S,消耗的费用为 TotalCost , 去过的地点为 Count
题目要求 时间、中转、费用最优,以及能够参与更多的景点,同样考虑构造一个函数进行最优化处理
其函数的设计为
(TotalCost * Percent[2] + BestVal.val * Percent[3]) / Count
从而找到最优的方案
*/