1. 程式人生 > 其它 >【牛客】網易互娛(遊戲)2021年-遊戲研發/初級遊戲研發/

【牛客】網易互娛(遊戲)2021年-遊戲研發/初級遊戲研發/

第一題

一位冒險者進入了一個迷宮中尋寶。他手上已經擁有了一份這個迷宮的地圖,其中標註了迷宮的完整結構以及迷宮中每個寶箱所在的位置。

迷宮的地圖是一個由m*n個格子組成的平面圖,縱向的上下方向上每列有m個格子,橫向的左右方向上每行有n個格子,其中每個格子為不能進入的障礙格或可以進入的通行格。如果有兩個上下相鄰或左右相鄰的通行格,則可以從其中一個通行格走到另一個通行格。每個寶箱放置在不同的通行格中。

他的目標是收集到這個迷宮裡的所有寶箱,因此他給自己在這個迷宮中尋寶制定瞭如下策略:

  1. 計算出離他當前位置曼哈頓距離最小的未收集寶箱,如果有多個就選定最小編號那個,記為k號寶箱;

  2. 如果他當前位置無法到達k號寶箱,則收集失敗,流程結束;否則,計算出他當前位置到k號寶箱的最短路徑長度,並且按上下左右的次序依次計算出如果向這個方向走一格通行格之後,到k號寶箱的最短路徑長度是否有縮短,如果有縮短則往這個方向走一格;

  3. 如果他當前所在位置有未收集寶箱,就收集這個寶箱。如果所有寶箱已經被收集,則收集完成。否則回到第1步並重復執行。

其中迷宮中兩個位置的曼哈頓距離,是指這兩個位置在上下方向的行差,加上左右方向的列差。如果用公式表示,如果兩個位置分別為(x, y)和(u, v),則這兩個位置的曼哈頓距離為|x-u|+|y-v|。
兩個位置間的一條路徑,是指從其中一個位置開始,通過若干個相鄰通行格,走到另一個位置,其中經過的通行格順序。兩個位置的最短路徑,是指這兩個位置的所有路徑中,通過的通行格數量最少的路徑。兩個位置的最短路徑長度,是指沿這兩個位置的最短路徑走的格數。

問在這種策略下收集所有寶箱,他需要走多少格?

點選檢視程式碼
#include<iostream>
#include<string.h>
#include<vector>
#include<queue>
#include<math.h>
using namespace std;
#define nullptr NULL
struct Pos
{
    int x = 0, y = 0, d = 0;
    Pos& set(int _x, int _y, int _d = 0)
    {
        x = _x;
        y = _y;
        d = _d;
        return *this;
    }
}node[10];
// 寶箱到每個點的最小距離
int ***dp = nullptr;
char map[50][50];
bool vis[50][50];
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
 
void InitDp(int boxn, int x, int y)
{
    if(dp != nullptr)
        delete[] dp;
     
    dp = new int**[x];
    for(int i = 0; i < x; ++i)
        dp[i] = new int*[y];
         
    for(int i = 0; i < x; ++i)
    {
        for(int j = 0; j < y; ++j)
        {
            dp[i][j] = new int[boxn];
            memset(dp[i][j], 0, sizeof(int) * boxn);
        }
    }
}
// 計算寶箱與每個點的最小距離
void Bfs(int no, Pos pos, const Pos& s)
{
    memset(vis, 0, sizeof(vis));
    queue<Pos> tmp;
    tmp.push(pos);
    vis[pos.x][pos.y] = true;
     
    int nx, ny;
    while(!tmp.empty())
    {
        Pos &pos = tmp.front();
        tmp.pop();
        for(int i = 0; i < 4; ++i)
        {
            nx = pos.x + dx[i];
            ny = pos.y + dy[i];
            if(nx >= 0 && nx < s.x && ny >= 0 && ny < s.y && !vis[nx][ny] && map[nx][ny] != '#')
            {
                vis[nx][ny] = true;
                dp[nx][ny][no] = pos.d + 1;
                tmp.push(Pos().set(nx,ny,pos.d+1));
            }
        }
    }
    /*for(int i = 0; i < s.x; ++i)
    {
        for(int j = 0; j < s.y; ++j)
            cout << dp[i][j][no] << ends;
        cout << endl;
    }
    cout << endl;*/
}
 
int CheackSearch(Pos& per, int d, const Pos& s, const int& boxn)
{
    memset(vis, 0, sizeof(vis));
    queue<Pos> tmp;
    tmp.push(per);
    vis[per.x][per.y] = true;
    int srh = 0;
    int nx, ny;
     
    while(!tmp.empty())
    {
        Pos &pos = tmp.front();
        tmp.pop();
         
        if(isalnum(map[pos.x][pos.y]))
        {
            if(node[map[pos.x][pos.y] - '0'].d == 0)
            {
                memset(vis, 0, sizeof(vis));
                // 寶箱i被收集
                node[map[pos.x][pos.y] - '0'].d = 1;
                vis[pos.x][pos.y] = true;
                ++srh;
            }
        }
        if(srh == boxn)
            return pos.d;
         
        int no;
        int maxMhd = 10000;
        // 查詢與當前位置曼哈頓距離最小的寶箱
        for(int i = 0; i < boxn; ++i)
        {
            int m = abs(pos.x - node[i].x) + abs(pos.y - node[i].y);
            if(node[i].d != 1 && m < maxMhd)
            {
                maxMhd = m;
                no = i;
            }
        }
         
        for(int i = 0; i < 4; ++i)
        {
            nx = pos.x + dx[i];
            ny = pos.y + dy[i];
             
            if(nx >= 0 && nx < s.x && ny >= 0 && ny < s.y)
            {
                if(!vis[nx][ny]
                   && map[nx][ny] != '#'
                   && dp[nx][ny][no] < dp[pos.x][pos.y][no]) // 判斷下一步走的最小點是否為必當前最小路徑更小
                {
                    vis[nx][ny] = true;
                    tmp.push(Pos().set(nx, ny, pos.d+1));
                }
                 
            }
        }
    }
    return -1;
}
 
int main()
{
    int n, boxcnt;
    Pos per, size;
    cin >> n;
    while(n--)
    {
        boxcnt = 0;
        scanf("%d %d\n", &size.x, &size.y);
        // 初始化本輪map
        memset(map, 0, sizeof(map));
        for(int i = 0; i < size.x; ++i)
        {
            for(int j = 0; j < size.y; ++j)
            {
                cin >> map[i][j];
                if(map[i][j] == '*')
                    per.set(i, j);
                else if(isalnum(map[i][j]))
                {
                    node[map[i][j] - '0'].set(i, j);
                    ++boxcnt;
                }
            }
        }
        InitDp(boxcnt, size.x, size.y);
        for(int i = 0; i < boxcnt; ++i)
            Bfs(i, node[i], size);
        int ans = CheackSearch(per, 0, size, boxcnt);
        cout << ans << endl;
    }
    delete[] dp;
    return 0;
}

第二題

遊戲工程師小明購買了VR裝置之後愛上了體感遊戲,而最近他把他的業餘時間花在了一款叫十字斬的遊戲裡。當你戴上VR眼鏡啟動遊戲後,先選擇一首音樂,然後會發現有一個NN的方陣呈現在你的眼前,方陣的每個格子上都有一個數字。然後伴隨著音樂節拍,你需要按照時機對方陣進行一次十字斬擊(同時斬掉一行和一列,而且選好了行列後不能斬到選定行列之外的格子)。斬擊完了之後,矩陣會收縮成一個(N-1)(N-1)的方陣。

特別的,若該次十字斬斬到的格子數字和是本次所有十字可能裡最大的,則會獲得一個Perfect,如果N次十字斬都是Perfect,則可以獲得FullCombo的成就。但小明的心算能力不行,至今還未能獲得FullCombo的成就。所幸初始數字方陣與音樂是一一對應的,所以小明可以通過預先計算十字斬的位置然後背下來,遊玩的時候根據記憶去進行十字斬位置的選擇即可。

小明上了一天班已經不想寫程式碼了,所以他拜託你來寫一個程式為他計算出十字斬的方案。

點選檢視程式碼
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
    int n;
    cin >> n;
    unsigned int **p = new unsigned int*[n];
    unsigned int *rowSum = new unsigned int[n];
    unsigned int *colSum = new unsigned int[n];
    memset(rowSum, 0, n * sizeof(unsigned int));
    memset(colSum, 0, n * sizeof(unsigned int));
     
    for(int i = 0; i < n; ++i)
        p[i] = new unsigned int[n];
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            cin >> p[i][j];
            rowSum[i] += p[i][j];
            colSum[j] += p[i][j];
        }
    }
    bool *cutRow = new bool[n];
    bool *cutCol = new bool[n];
    memset(cutRow, 0, n * sizeof(bool));
    memset(cutCol, 0, n * sizeof(bool));
     
    for(int k = 0; k < n; k++)
    {
        int mx = 0, my = 0, max = 0;
             
        for(int i = 0; i < n; ++i)
        {
            if(cutRow[i])
                continue;
            for(int j = 0; j < n; ++j)
            {
                if(cutCol[j])
                    continue;
                int tmp = rowSum[i] + colSum[j] - p[i][j];
                if(tmp > max)
                {
                    max = tmp;
                    mx = i;
                    my = j;
                }
            }
        }
         
        int ax = 0, ay = 0;
        for(int i = 0; i < n; ++i)
        {
            if(!cutCol[i])
            {
                colSum[i] -= p[mx][i];
                if(i < my) ay++;
            }
            if(!cutRow[i])
            {
                rowSum[i] -= p[i][my];
                if(i < mx) ax++;
            }
        }
        cutRow[mx] = cutCol[my] = true;
        rowSum[mx] = colSum[my] = 0;
        printf("%d %d\n", ax + 1, ay + 1);
    }
    delete[] cutRow;
    delete[] cutCol;
}

第三題

七星不靠是中國麻將競賽規則的番種,胡牌時由東南西北中發白7張,外加其他花色的147、258、369不相連的牌型,且沒有將牌而組成。

--百度百科

七星不靠中的七星是指:東西南北中發白,也就是牌中必須有這七張。而其它牌按下述的來拼全:

東西南北中發白+147萬+258餅+369條

東西南北中發白+147萬+258條+369餅

東西南北中發白+147條+258萬+369餅

東西南北中發白+147條+258餅+369萬

東西南北中發白+147餅+258條+369萬

東西南北中發白+147餅+258萬+369條

由於胡牌時只需要14張牌,而上述組合均有16張,那麼除了東西南北中發白必須有外,其它三色可以隨便去掉兩張,都可以組成七星不靠。

我們的任務是,假設我們的14張牌中已經包含了東西南北中發白這7張牌,另外的牌都是萬餅條的序數牌,給出另外的這7張牌,判斷是否能組成七星不靠。

點選檢視程式碼
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

int main()
{
	int n;
	cin >> n;

	const char *ans = "YES";
	//FILE* fp;
	//fopen_s(&fp, "1.h", "w");
	while (n--)
	{
		char pai[10] = { 0 };
		short tmp = 0, t, b, w;
		t = b = w = 0;
		for (int i = 0; i < 7; i++)
		{
			int c;
			char a;
			scanf("%d%c", &c, &a);
			pai[c] = a;
			switch (a)
			{
			case 'W': w++;break;
			case 'B': b++;break;
			case 'T': t++;break;
			}
		}
		for (int i = 1; i < 10; ++i)
		{
			if (pai[i] != 0 && pai[i] == pai[i - 1])
				ans = "NO";
			if (pai[i] != 0)
				tmp++;
		}
		if (tmp < 7 || w > 3 || b > 3 || t > 3)
			ans = "NO";
		std::cout << ans << endl;
		//fwrite(ans, strlen(ans), 1, fp);
		//fwrite("\n", 1, 1, fp);
		ans = "YES";
	}
	system("pause");
}

第四題

明明最近迷上了《明日之後》這款遊戲。在這款遊戲中有一個配方製造系統,玩家可以利用手中的資源和材料,來製造武器和道具。例如,玩家如果需要製造一個小木櫃,需要3塊木板,而製造一塊木板,又需要120個木頭和2個小樹枝,並且需要走到建築加工臺前製作。而採集木頭和小樹枝又需要一定的時間。

玩了一段時間之後,明明開始好奇在遊戲中做什麼最花時間。雖然遊戲中已經有標明每個物品的製造時間,但是明明更想通過自己的遊戲經歷來統計實際需要的時間。明明根據自己的操作,記錄下了自己遊戲中每個操作事件的開始和結束時間,按時間順序彙總成了一張表,如下所示:
從上表可以看出,“製造1個小木櫃”這個操作,總共用時2000-1=1999秒時間,其中包含兩部分:製造3塊木板的耗時(1000-5=995秒)和自身的耗時(1999-995=1004秒)。同樣的,製造3塊木板的995秒耗時中,也包括3部分:收集360個木頭的耗時(20-10=10秒)、收集6個小樹枝的耗時(40-25=15秒)以及自身耗時(995-10-15=970秒)。在這些操作當中,“製造1個小木櫃”自身耗時1004秒,是所有操作中自身耗時最多的一個操作。

明明想知道自己做的這些操作中,哪個操作自身所花的時間是最多的。給出這張事件記錄表,你可以幫明明計算一下嗎?

點選檢視程式碼
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
struct Data
{
	unsigned int time;
	unsigned int id;
};
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
		int cnt;
		cin >> cnt;
		stack<Data> data;
		vector<Data> dataFin;
		for(int i = 0; i < cnt; ++i)
		{
			Data t;
			int flag;
			scanf("%d %d %d", &t.time, &t.id, &flag);
			//cin >> t.time >> t.id >> flag;
			if(flag == 0)
				data.push(t);
			else
			{
				unsigned int tweenTime = 0;
				while(data.top().id != t.id)
				{
					Data &tmp = data.top();
					tweenTime += tmp.time;
					data.pop();
				}
				
				Data tmp = data.top();
				data.pop();
				tmp.time = t.time - tmp.time;
				data.push(tmp);
				tmp.time -= tweenTime;
				dataFin.push_back(tmp);
				
			}
		}
		unsigned int max = 0;
		for(int i = 1; i < dataFin.size(); ++i)
		{
			if(dataFin[i].time > dataFin[max].time || dataFin[i].time == dataFin[max].time && dataFin[i].id < dataFin[max].id)
				max = i;
		}
		cout << dataFin[max].id << endl;
	}
}