【程式設計測試題】阿里巴巴2019年提前批程式設計題

HelloZEX發表於2018-08-09

光明小學的小朋友們要舉行一年一度的接力跑大賽了,但是小朋友們卻遇到了一個難題:設計接力跑大賽的線路,你能幫助他們完成這項工作麼? 
光明小學可以抽象成一張有N個節點的圖,每兩點間都有一條道路相連。光明小學的每個班都有M個學生,所以你要為他們設計出一條恰好經過M條邊的路徑。 
光明小學的小朋友們希望全盤考慮所有的因素,所以你需要把任意兩點間經過M條邊的最短路徑的距離輸出出來以供參考。* 
你需要設計這樣一個函式: 
res[][] Solve( N, M, map[][]); 
注意:map必然是N * N的二維陣列,且map[i][j] == map[j][i],map[i][i] == 0,-1e8 <= map[i][j] <= 1e8。(道路全部是無向邊,無自環)2 <= N <= 100, 2 <= M <= 1e6。要求時間複雜度控制在O(N^3*log(M))。 
map陣列表示了一張稠密圖,其中任意兩個不同節點i,j間都有一條邊,邊的長度為map[i][j]。N表示其中的節點數。 
你要返回的陣列也必然是一個N * N的二維陣列,表示從i出發走到j,經過M條邊的最短路徑 
你的路徑中應考慮包含重複邊的情況。 
這個題目要求在30分鐘內解決,臣妾做不到的說。先來看看題目解析。 
題目解析:該題目要求在M次選擇中,計算出i與j之間距離最短的值,是一種動態規劃題型,因此我們用動態規劃方式解答(C++):



#include <iostream>
using namespace std;
void Solve(int N,int M,int** map,int** res)
{
    for(int i=0;i<N;i++)
    {
        int **E=new int*[N];
        for(int j=0;j<N;j++)
            E[j]=new int[M];     //建立存放動態結果的矩陣  
        for(j=0;j<N;j++)
            if(i!=j)
                E[j][0]=map[i][j];
            else
                E[j][0]=100000000;
        for(int k=1;k<M;k++)
            for(j=0;j<N;j++)
                E[j][k]=100000000;
        for(k=1;k<M;k++)
        {
            for(j=0;j<N;j++)
            {
                for(int u=0;u<N;u++)
                {
                    if(u!=j)
                    {   
                        if(E[j][k]>E[u][k-1]+map[u][j])
                            E[j][k]=E[u][k-1]+map[u][j];
                    }
                }
            }
        }
        for(j=0;j<N;j++)
            res[i][j]=E[j][M-1];
    }
};
int main()
{
    int N,M;   //輸入N個結點,M個路徑
    //建立二維地圖
    int i,j;
    cout<<"請輸入結點數N,路徑數M和地圖Map:";
    cin>>N;
    cin>>M;
    int **map=new int*[N];
    int **res=new int*[N];
    for(i=0;i<N;i++)
    {
        map[i]=new int[N];
        res[i]=new int[N];
    }
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            cin>>map[i][j];
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            res[i][j]=100000000;
    Solve(N,M,map,res);
    for(i=0;i<N;i++)
    {       
        for(int j=0;j<N;j++)
            cout<<res[i][j]<<"  ";
        cout<<endl;
    }

    for(i=0;i<N;i++)
        delete[] res[i];
    delete[] res;
    return 0;
}

以上是動態規劃過程,我們從中可以看一下for巢狀的個數,可以發現,該演算法的複雜度為O(N^3*M),與要求的不符合,如何做到時間複雜度為O(N^3logM)呢? 
我們觀察到,我們求取步驟中,其實做了很多的無用功,比如我有4個小朋友,我要求得4段距離總和最小,如果用上面的程式,先求得2段距離最小,然後3段,然後4段,其實在後面基本上都是在原本地圖矩陣上查詢,地圖矩陣i與j之間的距離是1段的距離,當我們計算出2段距離最小時,其實可以將該矩陣作為新的地圖,即i與j之間是2段的距離,以此類推,我可以通過4段距離最小矩陣求得8段距離矩陣。此時我們需要用到遞迴,程式如下:

void Solve(int N,int M,int** map,int** res)
{
    int i,j,k;
    int a=M/2;
    int b=M-a*2;

    int **E=new int*[N];
    for(i=0;i<N;i++)
        E[i]=new int[N];
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            E[i][j]=100000000;

    if(a==1)
    {
        for(i=0;i<N;i++)
            for(j=0;j<N;j++)
                for(k=0;k<N;k++)
                    if(i!=k&&j!=k&&res[i][j]>map[i][k]+map[k][j])
                        res[i][j]=map[i][k]+map[k][j];
        if(1==b)
        {
            for(i=0;i<N;i++)
                for(j=0;j<N;j++)
                    for(k=0;k<N;k++)
                        if(j!=k&&E[i][j]>res[i][k]+map[k][j])
                            E[i][j]=map[k][j]+res[i][k];
            for(i=0;i<N;i++)
                for(j=0;j<N;j++)
                    res[i][j]=E[i][j];
        }
        for(i=0;i<N;i++)
            delete[] E[i];
        delete[] E;
        return;
    }
    Solve(N,a,map,res);
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            for(k=0;k<N;k++)
                if(E[i][j]>res[i][k]+res[k][j])
                    E[i][j]=res[i][k]+res[k][j];
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
        {
            res[i][j]=E[i][j];
            E[i][j]=100000000;
        }

    if(b==1)
    {
        for(i=0;i<N;i++)
            for(j=0;j<N;j++)
                for(k=0;k<N;k++)
                    if(j!=k&&E[i][j]>res[i][k]+map[k][j])
                        E[i][j]=res[i][k]+map[k][j];
        for(i=0;i<N;i++)
            for(j=0;j<N;j++)
                res[i][j]=E[i][j];
    }
    for(i=0;i<N;i++)
        delete[] E[i];
    delete[] E;
}

int main()
{
    int N,M;   //輸入N個結點,M個路徑
    //建立二維地圖
    int i,j;
    cout<<"請輸入結點數N,路徑數M和地圖Map:";
    cin>>N;
    cin>>M;
    int **map=new int*[N];
    int **res=new int*[N];
    for(i=0;i<N;i++)
    {
        map[i]=new int[N];
        res[i]=new int[N];
    }
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            cin>>map[i][j];
    for(i=0;i<N;i++)
        for(j=0;j<N;j++)
            res[i][j]=100000000;
    Solve(N,M,map,res);
    for(i=0;i<N;i++)
    {       
        for(int j=0;j<N;j++)
            cout<<res[i][j]<<"  ";
        cout<<endl;
    }

    for(i=0;i<N;i++)
        delete[] res[i];
    delete[] res;
    return 0;
}

我們在每一層遞迴中,都傳入了地圖資訊,是因為小朋友數M的個數不一定是2的n次方,若無法整除2,那麼就需要地圖map的幫助啦,此演算法的複雜度可以保證在O(N^3*logM).

有什麼問題的話,歡迎留言討論。 
本文為原創文章,歡迎轉載,轉載請註明出處!

原文地址: https://blog.csdn.net/jiulexiaoyao/article/details/81228934

 



//複雜度N^3 * logM

void Solve(int N, int M, vector<vector<int>> &map, vector<vector<int>> &R)
{
	//動態規劃
	int i, j, k;
	int a = M / 2;
	int b = M - a * 2;

	vector<int> T(N, (int)1e6);
	vector<vector<int>> E(N, T);	//動態中間結果

	if (a == 1)
	{
		for (i = 0; i < N; i++)
			for (j = 0; j < N; j++)
				for (k = 0; k < N; k++)
					if (i != k&&j != k&&R[i][j] > map[i][k] + map[k][j])
						R[i][j] = map[i][k] + map[k][j];
		if (1 == b)
		{
			for (i = 0; i < N; i++)
				for (j = 0; j < N; j++)
					for (k = 0; k < N; k++)
						if (j != k&&E[i][j] > R[i][k] + map[k][j])
							E[i][j] = map[k][j] + R[i][k];

			for (i = 0; i < N; i++)
				for (j = 0; j < N; j++)
					R[i][j] = E[i][j];
		}
		return;
	}

	Solve(N, a, map, R);

	for (i = 0; i < N; i++)
		for (j = 0; j < N; j++)
			for (k = 0; k < N; k++)
				if (E[i][j] > R[i][k] + R[k][j])
					E[i][j] = R[i][k] + R[k][j];

	for (i = 0; i < N; i++)
		for (j = 0; j < N; j++)
		{
			R[i][j] = E[i][j];
			E[i][j] = (int)1e6;
		}

	if (b == 1)
	{
		for (i = 0; i < N; i++)
			for (j = 0; j < N; j++)
				for (k = 0; k < N; k++)
					if (j != k&&E[i][j] > R[i][k] + map[k][j])
						E[i][j] = R[i][k] + map[k][j];

		for (i = 0; i < N; i++)
			for (j = 0; j < N; j++)
				R[i][j] = E[i][j];
	}
}

int main()
{
	int N = 3;	//N個結點
	int M = 6;  //M個路徑
	//cin >> N;
	//cin >> M;

	int i, j;	
	vector<int> T(N, 0);
	vector<vector<int>> map(N, T);	//建立二維地圖
	vector<vector<int>> R(N, T);	//建立二維表

	//輸入資料
	int Temp[9] = {0,2,3,2,0,1,3,1,0};

	for (i = 0; i < N; i++)
	{
		for (j = 0; j < N; j++)
		{
			R[i][j] = (int)1e6;
			//cin >> map[i][j];
			map[i][j] = Temp[i * 3 + j];
		}
	}

	//求解
	Solve(N, M, map, R);

	//列印
	for (i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			cout << R[i][j] << "  ";
		}
		cout << endl;
	}
	return 0;
}

//複雜度N^3 * M

#include <iostream>
using namespace std;
void Solve(int N, int M, vector<vector<int>>& map, vector<vector<int>>& res)
{

	for (int i = 0; i < N; i++)
	{
		vector<int> T(M, (int)1e6);
		vector<vector<int>> E(N, T);	//動態中間結果

		for (int j = 0; j < N; j++)
			if (i != j)
				E[j][0] = map[i][j];
			else
				E[j][0] = 100000000;
		for (int k = 1; k < M; k++)
			for (int j = 0; j < N; j++)
				E[j][k] = 100000000;
		for (int k = 1; k < M; k++)
		{
			for (int j = 0; j < N; j++)
			{
				for (int u = 0; u < N; u++)
				{
					if (u != j)
					{
						if (E[j][k] > E[u][k - 1] + map[u][j])
							E[j][k] = E[u][k - 1] + map[u][j];
					}
				}
			}
		}
		for (int j = 0; j < N; j++)
			res[i][j] = E[j][M - 1];
	}
};
int main()
{
	int N = 3;
	int M = 2;   //輸入N個結點,M個路徑
				//建立二維地圖

	int i, j;	
	vector<int> T(N, 0);
	vector<vector<int>> map(N, T);	//建立二維地圖
	vector<vector<int>> R(N, T);	//建立二維表

	//輸入資料
	int Temp[9] = {0,2,3,2,0,1,3,1,0};

	for (i = 0; i < N; i++)
	{
		for (j = 0; j < N; j++)
		{
			R[i][j] = (int)1e6;
			//cin >> map[i][j];
			map[i][j] = Temp[i * 3 + j];
		}
	}


	Solve(N, M, map, R);
	for (i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
			cout << R[i][j] << "  ";
		cout << endl;
	}

	return 0;
}

有問題可以留言!

相關文章