城市旅游購物交通咨詢模擬(最小環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
從而找到最優的方案

*/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM