問題描述
農夫約翰正在針對一個新區域的牛奶配送合同進行研究。他打算分發牛奶到T個城鎮(標號爲1..T),這些城鎮通過R條標號爲(1..R)的道路和P條標號爲(1..P)的航路相連。
每一條公路i或者航路i表示成連接城鎮Ai(1<=A_i<=T)和Bi(1<=Bi<=T)代價爲Ci。每一條公路,Ci的範圍爲0<=Ci<=10,000;由於奇怪的運營策略,每一條航路的Ci可能爲負的,也就是-10,000<=Ci<=10,000。
每一條公路都是雙向的,正向和反向的花費是一樣的,都是非負的。
每一條航路都根據輸入的Ai和Bi進行從Ai->Bi的單向通行。實際上,如果現在有一條航路是從Ai到Bi的話,那麼意味着肯定沒有通行方案從Bi回到Ai。
農夫約翰想把他那優良的牛奶從配送中心送到各個城鎮,當然希望代價越小越好,你可以幫助他嘛?配送中心位於城鎮S中(1<=S<=T)。
輸入格式
輸入的第一行包含四個用空格隔開的整數T,R,P,S。
接下來R行,描述公路信息,每行包含三個整數,分別表示Ai,Bi和Ci。
接下來P行,描述航路信息,每行包含三個整數,分別表示Ai,Bi和Ci。
輸出格式
輸出T行,分別表示從城鎮S到每個城市的最小花費,如果到不了的話輸出NO PATH。
樣例輸入
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
樣例輸出
NO PATH
NO PATH
5
0
-95
-100
數據規模與約定
對於20%的數據,T<=100,R<=500,P<=500;
對於30%的數據,R<=1000,R<=10000,P<=3000;
對於100%的數據,1<=T<=25000,1<=R<=50000,1<=P<=50000。
解答:主要運用深度優先搜索和權值約束。相關知識參見http://blog.163.com/zhoumhan_0351/blog/static/3995422720098342257387/
下面有兩個程序可供參考
實現的Java算法如下
(由於採用遞歸,在測試大規模數據時會報java.lang.StackOverflowError錯誤。)
import java.util.Scanner;
import java.util.Stack;
public class MinCost {
static Stack<Integer> stack = new Stack<Integer>();//堆棧
static Stack<Integer> cost = new Stack<Integer>();//花費的堆棧
static int temp =0;
public static void main(String []args)
{
Scanner ss = new Scanner(System.in);
int T = ss.nextInt();
int min[] = new int[T+1];
int parents[] = new int[T+1];
int R = ss.nextInt();
int P = ss.nextInt();
int S = ss.nextInt();
int A[] = new int[2*R+P+1];
int B[] = new int[2*R+P+1];
int C[] = new int[2*R+P+1];
int a,b,c;
for(int i=1;i<=T;i++)//訪問標誌初始化
{
parents[i] = 0;
}
for(int i=1;i<=2*R;i++)
{
a = ss.nextInt();
b = ss.nextInt();
c = ss.nextInt();
A[i] = a;B[i] = b;C[i] = c;
i++;
A[i] = b;B[i] = a;C[i] = c;
}
for(int i=2*R+1;i<=2*R+P;i++)
{
A[i] = ss.nextInt();
B[i] = ss.nextInt();
C[i] = ss.nextInt();
}
stack.push(S);//將出發城鎮初始化
min[S] = 0;
cost.push(0);
int mini[] = DFS(A,B,C,min,parents);
for(int i=1;i<mini.length;i++)
{
if(i!=S&&mini[i]==0)
System.out.println("NOPATH");
else
System.out.println(mini[i]);
}
}
public static int[] DFS(int[] A,int[] B ,int[] C,int[] min,int[] parents)
{
for(int i=1;i<A.length;i++)
{
if(A[i]==stack.peek()&&checkParents(B[i],parents)&&check(B[i]))
{
stack.push(B[i]);
cost.push(cost.peek()+C[i]);
parents[stack.peek()] = A[i];
if(min[B[i]]==0||min[B[i]]>cost.peek())
{
min[B[i]] = cost.peek();
}
DFS(A,B,C,min,parents);
}
}
if(stack.peek()==stack.get(0))
return min;
cost.pop();
stack.pop();
DFS(A,B,C,min,parents);
return min;
}
public static boolean check(int n)
{
boolean flag = true;
for(int i=0;i<stack.size();i++)
{
if(n==stack.get(i))
{
flag = false;
break;
}
}
return flag;
}
public static boolean checkParents(int n,int[] parents)
{
if(parents[n]==stack.peek())
return false;
else
return true;
}
}
非遞歸程序(木有大量測試數據)
import java.util.Scanner;
import java.util.Stack;
public class TT {
public static int S;
public static void main(String []args)
{
int minx[] = DFS();
for(int i=1;i<minx.length;i++)
{
if(i!=S&&minx[i]==0)
System.out.println("NO PATH");
else
System.out.println(minx[i]);
}
}
public static int[] DFS()
{
Scanner ss = new Scanner(System.in);
int T = ss.nextInt();//輸入城市數
int R = ss.nextInt();//輸入道路數
int P = ss.nextInt();//輸入航路數
S = ss.nextInt();//輸入出發城鎮
int min[] = new int[T+1];//記錄到每個城鎮的最小花費
int C[][] = new int[T+1][T+1];//C[i][j]表示記錄從i城鎮到j城鎮的花費
boolean visit[] = new boolean[T+1];//標記每個城市是否被訪問過
Stack<Integer> stack = new Stack<Integer>();//堆棧
Stack<Integer> cost = new Stack<Integer>();//花費的堆棧
int temp =0;
Stack<Integer>[] city = new Stack[T+1];//記錄每個每個城鎮爲出發點的所有道路或航路
int flag[] = new int[T+1];//記錄該城鎮爲訪問節點爲i時的下一個訪問節點在city[]中的位置
for(int i=1;i<city.length;i++)//將city[]數組初始化
{
city[i] = new Stack<Integer>();
}
for(int i=1;i<visit.length;i++)//將visit[]數組初始化
for(int j=1;j<visit.length;j++)
{
visit[i] = false;
}
int m,n,k;
for(int i=1;i<=R;i++)//將city[]數組賦值
{
m = ss.nextInt();
n = ss.nextInt();
k = ss.nextInt();
city[m].add(n);C[m][n]=k;
city[n].add(m);C[n][m]=k;
}
for(int i=1;i<=P;i++)//將city[]數組賦值
{
m = ss.nextInt();
n = ss.nextInt();
k = ss.nextInt();
city[m].add(n);C[m][n]=k;
}
stack.push(S);//將出發城鎮壓入堆棧
min[S] = 0;//將出發城鎮的最小花費置0
cost.push(0);//花費初始壓入0
visit[S] = true;//將出發城鎮訪問位置true
/*
* 由深度優先搜索對所有路徑進行遍歷
*/
int i;
while(true)
{
while(true)
{
int a = stack.peek();//取出訪問節點
for(i=flag[a];i<city[a].size();i++)//在city[]遍歷訪問
{
int b =city[a].get(i);
if(!visit[b])//判斷下一個節點是否被訪問過
{
/*
* 如果下個節點可被訪問。則將相應數據壓入堆棧.並把被城鎮的city[]數組的訪問位flag[]+1
*/
cost.push(cost.peek()+C[a][b]);//將到下一個節點所需花費壓入cost堆棧
flag[a]++;
visit[b] = true;
stack.push(b);
int c = stack.peek();
System.out.println(c+" "+min[c]+" "+cost.peek());
if(min[c]==0||min[c]>cost.peek())//判斷是否到該訪問節點是否是已有路徑到該節點的最小花費
{
min[c] = cost.peek();
}
break;//退出 ,進去下一個節點的訪問
}
}
if(i==city[a].size())
break;//如果遍歷該節點的city[]數組,都沒有可訪問的下一個節點則退出,進行回退操作
}
/*
* 如果沒有找到下一個節點。則回退,相應數據出棧
*/
int d = stack.peek();
if(d==stack.get(0))//如果將要出棧數據爲出發城鎮則遍歷結束。退出。
break;
visit[d]=false;
cost.pop();
stack.pop();
}
return min;
}
}