做图论就是好慢啊……比做出来更难的就是,找不到自己错在哪里了……
贴个题干!
简言之,就是你是一个消防队,你在A,B失火了,让你选择最短的路径,并统计这样的路有几条,并通知途径城市的消防队加入救援,最短路径中能号召的最多队伍是多少。
听起来不难吧……
上学期的我看到这道题要哭了,完全没有思路,现在懂了一些图论的知识,所以有了一点想法。
我用的是弗洛伊德算法,就是任选两点,不断往里面加节点(也就是加拐弯的地方),尝试寻找更短的路径,也就是说要n*n*n层循环,找出任意两点间的最短距离。
核心代码为:
for (k = 0; k <= citynum; k++)
{
for (i = 0; i <= citynum; i++)
{
for (j = 0; j <= citynum; j++)
{
if (k == i || k == j || i == j)
continue;
if (road[i][k]<INF&&road[k][j]<INF&&road[i][j] == road[i][k] + road[k][j])//同等长度路径数目增加
{
num[i][j]++;//寻找i j 两个节点间最小距离
rt[i][j] = max(rt[i][j], rt[i][k] + rt[k][j]-resnum[k]);//记录最大救援队数量
}
else if (road[i][k]<INF&&road[k][j]<INF&&road[i][j]>road[i][k] + road[k][j])//同等长度的路径
{
road[i][j] = road[i][k] + road[k][j];
rt[i][j] = max(rt[i][j], rt[i][k] + rt[k][j]-resnum[k]);
num[i][j] = 1;//新的最短路径
}
}
}
}
为了完成题目,统计了等长条件下的路径长度,原版只要找出小于的情况即可。
同时要记录最短路径条件下,最多救援队的数目。
rt[i][j] = max(rt[i][j], rt[i][k] + rt[k][j]-resnum[k]);//记录最大救援队数量
二者取最大,同时记得减去过度节点k本身的救援队数目,不然结果就是i k k j三处救援队数目的总合……
然后考虑两种特殊情况,一个是救援地就是所在地,路径为1 救援队数目就是所在地的救援队数目;第二种就是二者有直达路线,这种情况,仍需考虑有更短的捷径出现的可能。
然鹅,这道题还是只有13/25,不知道错在哪里了……这个很蛋疼……
#include<iostream>
#include<stack>
#include<algorithm>
#include<string.h>
using namespace std;
#define INF 999999
int main()
{
//cout << INF << "uuuuuuuuu" << endl;
int citynum, roadnum, home, sos;
cin >> citynum >> roadnum;
cin >> home >> sos;//城市数目,道路数目,出发点,目的地
int resnum[500] ;
memset(resnum, 0, sizeof(resnum));//矩阵初始化
int i = 0, res;
while (i<citynum)
{
cin >> res;
resnum[i] = res;
i++;
}
int go, to, l;
static int road[500][500] ;//初始化两点距离
memset(road, INF, sizeof(road));
static int num[500][500] ;//每两个节点间最短路径的数量;
memset(num, 0, sizeof(num));
static int rt[500][500];//每两个节点间的救援队数量;
memset(rt, 0, sizeof(rt));
//cout << rt[1][2] << "ssssssss" << endl;
i = 0;
while (i<roadnum)
{
cin >> go >> to >> l;
road[go][to] = l;//录入两点间距
road[to][go] = l;
num[go][to] = 1;//两个节点间最短路径条数初始化
num[to][go] = 1;
rt[go][to] = resnum[go] + resnum[to];//两个节点间救援队数目初始化
rt[to][go] = rt[go][to];
i++;
}
if (home == sos)//如果起始位置一致
{
cout << "1 " << resnum[home];
system("PAUSE");
return 0;
}
int j, k;
int lol = INF; int team = 0;//初始化
int count = 0;
for (i = 0; i < citynum; i++)
{
road[i][i] = 0;
rt[i][i] = resnum[i];
}
if (road[home][sos] < INF)//如果存在直达
{
team=1;//初始化
lol = road[home][sos];
count = rt[home][sos];
}
for (k = 0; k <= citynum; k++)
{
for (i = 0; i <= citynum; i++)
{
for (j = 0; j <= citynum; j++)
{
if (k == i || k == j || i == j)
continue;
if (road[i][k]<INF&&road[k][j]<INF&&road[i][j] == road[i][k] + road[k][j])//寻找i j 两个节点间最小距离
{
num[i][j]++;//同等长度路径数目增加
rt[i][j] = max(rt[i][j], rt[i][k] + rt[k][j]-resnum[k]);//记录最大救援队数量
}
else if (road[i][k]<INF&&road[k][j]<INF&&road[i][j]>road[i][k] + road[k][j])//同等长度的路径
{
road[i][j] = road[i][k] + road[k][j];
rt[i][j] = max(rt[i][j], rt[i][k] + rt[k][j]-resnum[k]);
num[i][j] = 1;//新的最短路径
}
}
}
}
for (i = 0; i < citynum; i++)
{
for (j = 0; j < citynum; j++)
{
cout << road[i][j] << " ";
}
cout << endl;
}
cout << "*******************************************************" << endl;
cout<<num[home][sos]<<" "<<rt[home][sos];
system("PAUSE");
return 0;
}
然后昨晚和鸭子讨论了半天,为什么定义了500*500的矩阵就超出范围了???
然后发现是不能定义在main函数内,写在外面或者申请静态储存即可,static……
还有一点就是矩阵初始化,天真的我以为={0}就可以全部为0了,不知道谁给我的勇气Hhhhhhhh,正确示范,
memset(resnum, 0, sizeof(resnum));
resnum位置是数组名,中间值是初始值……
需要头文件<string.h>
大概就这些知识点吧……
华丽丽的分割线,又换了个解法,使用的是迪杰斯特拉算法,空间复杂度更小一点,来不及了,不是解释了。反正错的还是一样的测试点,(毕竟我的解题思路一样哈哈哈哈哈哈哈
贴个代码,依旧是13/25
/***对于无向图,输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数s,t;起点s,终点 t。***/
/***n和m为 0 时输入结束。(1<n<=1000, 0<m<100000, s != t) 输出:输出一行,有两个数, 最短距离及其花费。***/
#include <iostream>
#include <iomanip>
#include <algorithm>
#include<string.h>
using namespace std;
#define nmax 500
#define inf 99999999
struct Edge
{
int len;
int cost;
};
Edge edge[nmax][nmax];
int dst[nmax], spend[nmax], book[nmax], n, m, stNode, enNode,time1[nmax];
int main()
{
cin >> n >> m >> stNode >> enNode;
int a, b, i, j;
//构建邻接矩阵和最短路径数组
for (i = 0; i < n; i++)//初始化
{
for (j = 0; j < n; j++)
{
edge[i][j].cost = 0;
edge[i][j].len = inf;
}
time1[i] = 0;//道路数置0
edge[i][i].len = 0;//对角线元素
cin >> edge[i][i].cost;//对角线元素为各个城市的消防队数目
}
while (m--)
{
cin >> a >> b;//输入道路起始点
cin >> edge[a][b].len ;
edge[b][a].len = edge[a][b].len;//道路长度
edge[b][a].cost = edge[a][a].cost+edge[b][b].cost;//途径的消防队数量
edge[a][b].cost = edge[b][a].cost;
}
/*for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
cout << edge[i][j].len << " ";
}
cout << endl;
}*/
if(stNode==enNode)
{
cout<<"1 "<<edge[stNode][stNode].cost;
return 0;
}
for (i = 0; i < n; i++)
{
dst[i] = edge[stNode][i].len;
spend[i] = edge[stNode][i].cost;
//cout << dst[i] << " " << spend[i] << endl;
}
memset(book, 0, sizeof(book));
book[stNode] = 1;
//开始迪杰斯特拉算法,进行剩余n-1次松弛
int k;
for (k = 0; k < n ; k++)
{
//找离源点最近的顶点u
int minNode=0, min = inf;
for (i = 0; i < n; i++)
{
if (book[i] == 0 && min > dst[i] /* || min == dst[i]&& edge[stNode][min].cost > edge[stNode][i].cost*/)
{
min = dst[i];
minNode = i;
//cout << minNode <<endl;
}
}
//cout << setw(2) << minNode;
book[minNode] = 1;
//以中心点u为转折点来更新路径数组和花费数组
for (i = 0; i < n; i++)
{
if (i == minNode)
continue;
if (dst[i] == dst[minNode] + edge[minNode][i].len )
{
//同等更短路径
time1[i] ++;
//cout << "Q:" << i << " " << time1[i] << endl;
spend[i] = max(spend[i], edge[minNode][i].cost + edge[stNode][minNode].cost - edge[minNode][minNode].cost);
}
else if (book[i] == 0 && dst[i] > dst[minNode] + edge[minNode][i].len)//新的更短路径
{
dst[i] = dst[minNode] + edge[minNode][i].len;
spend[i] = max(spend[i], edge[minNode][i].cost + edge[stNode][minNode].cost - edge[minNode][minNode].cost);
time1[i] = 1;
}
}
}
cout << time1[enNode]<<" "<< spend[enNode] ;
system("PAUSE");
return 0;
}
对了答案还是不知道哪里错了哈哈哈哈假期问问老宋!