題目摘要
城市旅游購物交通咨詢模擬
【問題描述】
沈陽城內有若干旅游觀光景點和商業區。游客主要以公交車為交通工具出游。假設往返於每個景點和商業區的公交線路不少於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
從而找到最優的方案
*/