网易互娱2021校招08-12笔试4道题分析

目录

      • 1. 七星不靠牌型判断
      • 2. 十字斩
      • 3. 最长的任务时间
      • 4. 地图捡箱子(模拟)

如果发现有问题欢迎邮件交流。[email protected]

1. 七星不靠牌型判断

东南西北中发白+147/258/369的条/万/筒这16张牌可以胡。其中147/258/369这9张牌可缺任意2张。
给出7张数字牌,判断是否是七星不靠。
写的比较暴力。
先把7张牌按条万筒分类。
三类牌中的数字不能重复。
每一类不能超过3张牌。
每一类的数字排序后的差只能是3或6,且为6时这一类只能有2张牌。
满足以上条件即可。
输入

4
1T 4T 7T 2B 5B 8B 9W
1T 2T 3T 4T 5T 6T 7T
1B 2W 3T 4B 5W 6T 8W
2B 8B 5B 2B 6T 7W 4W

输出

YES
NO
YES
NO

#include 

using namespace std;

typedef pair<int, char> PIC;

void solve() {
    vector<vector<int> > P(3);
    
    for (int i=1;i<=7;i++) {
        char buf[10];
        scanf("%s", buf);
        switch (buf[1]) {
            case 'T':
                P[0].push_back(buf[0]-'0');
            break;
            case 'B':
                P[1].push_back(buf[0]-'0');
            break;
            case 'W':
                P[2].push_back(buf[0]-'0');
            break;
            default:
                cout << "NO" << endl;
                return;
        }
    }
    for (int i=0;i<3;i++) sort(P[i].begin(), P[i].end());
    bool ok = true;
    vector<bool> vis(10, false);
    for (int i=0;i<3;i++) {
        if (!ok) break;

        int s=P[i].size();
        if (s > 3) {
            ok = false;
            break;
        }
        for (int j=0;j<s;j++) {
            if (vis[P[i][j]]) {
                ok = false;
                break;
            }
            if (j > 0) {
                if (s==3 && P[i][j] - P[i][j-1] != 3) {
                    ok = false;
                    break;
                } else if (s==2 && (P[i][j] - P[i][j-1] != 3 && P[i][j] - P[i][j-1] != 6)) {
                    ok = false;
                    break;
                } 
            }
        }        
    }
    cout << (ok ? "YES":"NO") << endl;
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

2. 十字斩

给一个N*N的非负整数方阵。每次砍去一个元素和最大的十字(一行+一列),直到方阵消失,输出这N次砍去的行/列在剩余方阵中的坐标。

暴力模拟。
计原始方阵为M[i][j]。
首先算出每行的和 rsum[i],每列的和 csum[j]。
每次操作两重便利行的和与列的和,找一个最大的
r s u m [ i ] + c s u m [ j ] − M [ i ] [ j ] rsum[i] + csum[j] - M[i][j] rsum[i]+csum[j]M[i][j]
这里有一个问题,删除某行某列操作后,rsum和csum的下标会变化,需要保留M中的原始行列编号。实际计算的应该是
r s u m [ i ] + c s u m [ j ] − M [ i o r i ] [ j o r i ] rsum[i] + csum[j] - M[i_{ori}][j_{ori}] rsum[i]+csum[j]M[iori][jori]
实际定义的rsum和csum的元素是 pair i o r i i_{ori} iori j o r i j_{ori} jori 存在 second 即可。

#include 
#define X first
#define Y second

using namespace std;
const int MAXN = 510;
int M[MAXN][MAXN];
typedef pair<int,int> pii;
vector<pii> rsum;
vector<pii> csum;

int N;

int main() {
    cin >> N;
    rsum.resize(N+1);
    csum.resize(N+1);
    for (int i=0;i<=N;i++) rsum[i]=csum[i]=make_pair(0,i);

    for (int i=1;i<=N;i++) {
        for (int j=1;j<=N;j++) {
            scanf("%d", &M[i][j]);
            rsum[i].X+=M[i][j];
            csum[j].X+=M[i][j];
        }
    }
#ifdef LOCAL
    for (int i=1;i<=N;i++){
        for (int j=1;j<=N;j++) cout << M[i][j] << ' ';
        cout << endl;
    }
    for (int i=1;i<=N;i++) cout << rsum[i].X << ' ';
    cout << endl;
    for (int j=1;j<=N;j++) cout << csum[j].X << ' ';
    cout << endl;
#endif
    for (int k=1;k<=N;k++) {
        int maxs=0;
        int cut_i=1, cut_j=1;
        for (int i=1;i<rsum.size();i++) {
            for (int j=1;j<csum.size();j++) {
                int oi = rsum[i].second;
                int oj = csum[j].second;
#ifdef LOCAL
                cout <<"Debug: "<< rsum[i].X+csum[j].X-M[oi][oj] << " "
                << rsum[i].X << ' ' << csum[j].X << endl;
#endif 
                if (rsum[i].X+csum[j].X-M[oi][oj]>maxs) {
                    maxs=rsum[i].X+csum[j].X-M[oi][oj];
                    cut_i=i;
                    cut_j=j;
                }
            }
        }
        int oi = rsum[cut_i].Y;
        int oj = csum[cut_j].Y;
        for (int i=1;i<rsum.size();i++) {
            rsum[i].X-=M[rsum[i].Y][oj];
        }
        for (int j=1;j<csum.size();j++) {
            csum[j].X-=M[oi][csum[j].Y];
        }
        rsum.erase(rsum.begin()+cut_i);
        csum.erase(csum.begin()+cut_j);
        cout << cut_i << ' ' << cut_j << endl;
    }


    return 0;
}

3. 最长的任务时间

类似与我的世界的游戏,一些任务需要花费一定的时间并且依赖于一些子任务才能完成。给一个操作开始和结束的时间列表,计算花费了最长时间的任务是哪个。

输入样例:

1
8
1 1 0
5 2 0
10 3 0
20 3 1
25 4 0
40 4 1
1000 2 1
2000 1 1

通过栈可以获得子任务的父任务是哪个。
父任务实际花费的时间计算时要减去所有子任务的总花费时间
这里总花费时间和实际花费的时间需要区别。
答案要求的是实际花费的时间。
输入样例中,任务2的子任务包括任务3(耗时10)和任务4(耗时15)。
任务2实际花费的时间是 1000-5-(10+15)=970.
任务1实际花费的时间是 2000-1-(995)=1004.(而不是 2000-1-970=1029)。
用两个map分别存实际花费时间和包含子任务的总时间有20%左右的点超时。改成一个 map> 就都过了。

#include 
#define X first
#define Y second

using namespace std;
const int MAXN=100010;
int T;
typedef pair<int, int> pii;

void solve() {
    int N;
    scanf("%d", &N);
    int ans = 0;
    // total: cost
    // total.first: real time cost;
    // total.second: all time cost including subtasks
    map<int, pii> total; 
    map<int, int> fa;
    stack<int> st;
    for (int i=1;i<=N;i++) {
        int t; // time
        int e; // id
        int s; // 0 , 1
        scanf("%d%d%d", &t, &e, &s);
        if (s == 0) {
            total[e].X = total[e].Y = -t;
            fa[e] = st.empty() ? -1 : st.top();
            st.push(e);
        } else if (s == 1){
            total[e].X += t;
            total[e].Y += t;
            st.pop();
            if (fa[e] > 0)
                total[fa[e]].first -= total[e].second;
            if (total[e].first > total[ans].first) ans = e;
            else if (total[e].first == total[ans].first) ans = min(ans,e);
        }
    }
    cout << ans << endl;
#ifdef LOCAL
    cout << total[ans].first << ' ' << total[ans].second << endl;
#endif
}
int main() {
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

4. 地图捡箱子(模拟)

给一个m*n的地图,包括一个玩家,不超过10个箱子和障碍物坐标。
给了一个捡箱子的规则,模拟求拣完所有箱子的步数是多少。在给定规则下无法完成(包括陷入死循环)则输出-1.

规则大概是:
首先找到曼哈顿距离最小(都最小的取编号更小的)的未捡箱子记为k号箱子。
判断能否到达,不能则失败。
如可以到达,按照上下左右的顺序检查周围四个位置到k号箱子的距离是否减少。如减少则移动到该位置。
如当前位置有未捡的箱子则捡起。
回到第一步。

输入样例:

3
5 5
0…1
.#.#.

.#.#.
2…3
5 5
0…1
.#.#.
.#
.#.#.
2.#.3
5 5
…1
.####
…*…
####.
0…

输出:

16
-1
-1

第一组可以到达所有箱子。第二组不能到达3号箱子。
第三组会在该策略下不断循环且无法到达0或1号箱子。
实现模拟即可。在判断是否陷入循环时通过判断当前累积步数是否超过了一个比较大的数。

#include 
#define X first
#define Y second
#define MP make_pair

using namespace std;

const int MAXT = 5;
const int MAXL = 50+10;
const int INF  = 0x3f3f3f3f;
int T;
typedef pair<int, int> PII;

int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};

int bfs_dist(PII start, PII target, vector<vector<char> > &m, vector<vector<int> > &dis) {
    queue<PII> q;
    int M=m.size()-1; // M_ori + 1
    int N=m[0].size()-1;
    
    vector<vector<bool> > vis(M+2, vector<bool>(N+2, false));
    
    q.push(start);
    dis[start.X][start.Y] = 0;
    vis[start.X][start.Y] = true;
    while (!q.empty()) {
        PII cur = q.front(); q.pop();
        for (int d=0;d<4;d++) {
            int nx=cur.X+dx[d];
            int ny=cur.Y+dy[d];
            if (nx<=0||nx>M||ny<=0||ny>N||vis[nx][ny]||m[nx][ny]=='#') continue;
            if (nx==target.X && ny==target.Y) {
                return dis[cur.X][cur.Y] + 1;
            }
            q.push(MP(nx,ny));
            dis[nx][ny] = dis[cur.X][cur.Y] + 1;
            vis[nx][ny] = true;
        }
    }
    if (dis[target.X][target.Y] == INF) return -1;
    return dis[target.X][target.Y];
}

void solve() {
    int M, N;
    cin >> M >> N;
    int ans = 0;

    bool ok = true;
    PII people = MP(-1,-1);
    vector<PII> box(10);
    vector<bool> collected(10);
    vector<vector<char> > m(M+1,vector<char>(N+1, '#'));
    int ncollected = 0;
    int maxbox = -1;
    int nbox = -1;
    
    for (int i=1;i<=M;i++) for (int j=1;j<=N;j++) {
        cin >> m[i][j];
        if (m[i][j] == '*') {
            people = MP(i,j);
        } else if (isdigit(m[i][j])) {
            box[m[i][j]-'0'] = MP(i,j);
            if (m[i][j]-'0' > maxbox) maxbox = m[i][j] - '0';
        }
    }
    nbox = maxbox + 1;
#ifdef LOCAL
    // cout << "Input end. " << nbox << " boxes." << endl;
    // for (int i=1;i<=M;i++) {
    //     for (int j=1;j<=N;j++) {
    //         cout << m[i][j];
    //     }
    //     cout << endl;
    // }
#endif
    // calc mht distance, find min dist of uncollected box
    while (ok && ncollected < nbox && ans < MAXL * MAXL) {
        int min_dist = INF;
        int min_box = INF; // k_th box
        for (int i=0;i<=maxbox;i++) {
            if (!collected[i]) {
                int dist = abs(box[i].X-people.X)+abs(box[i].Y-people.Y);
                if (dist < min_dist) {
                    min_dist = dist;
                    min_box = i;
                }
            }
        }
        vector<vector<int> > dis(M+1, vector<int>(N+1, INF));
        if (bfs_dist(box[min_box], people, m, dis) == -1) {
            ans = -1;
            ok = false;
            break;
        } else {
            bool can_move = false;
            for (int d=0;d<4;d++) {
                int nx=people.X+dx[d];
                int ny=people.Y+dy[d];
                if (nx<=0||nx>M||ny<=0||ny>N||m[nx][ny]=='#') continue;
                if (dis[nx][ny] < dis[people.X][people.Y]) {
                    can_move = true;
                    people = MP(nx, ny);
                    ans++;
                    break;
                }
            }
            if (!can_move) {
                ans=-1;
                ok = false;
                break;
            }
            if (isdigit(m[people.X][people.Y]) && !collected[m[people.X][people.Y]-'0']) {
                collected[m[people.X][people.Y]-'0']=true;
                ncollected++;
            }
        }
    }
    if (ok) {
        if (ans < MAXL*MAXL)
            cout << ans << endl;
        else cout << -1 << endl;
    }
    else cout << -1 << endl;
}
int main() {
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

你可能感兴趣的:(算法,算法,游戏,数据结构,c++)