SMU 2026 Spring 天梯赛5题解

7-3 冠军魔术

题意:

给定初始纸牌数量 \(a\) 和推送次数 \(n\)。每次推送:纸牌变硬币数量不变,硬币变纸牌数量加倍。初始为纸牌。求 \(n\) 次后是纸牌还是硬币,以及数量。

思路:

推送奇数次后为硬币,偶数次后为纸牌。数量每次从硬币变纸牌时加倍,总共加倍次数为 \(n/2\) 次。因此数量为 \(a \times 2^{n/2}\)

代码

点击查看代码
void solve() {
	int x,y;
	cin>>x>>y;
	int p=0;
	while(p<y){
		p++;
		if(p%2==0){
			x*=2;
		}
	}
	cout<<y%2<<' '<<x<<endl;
}

7-4 Cassels方程

题意:

判断给定的三个正整数 \(x\), \(y\), \(z\) 是否满足方程 \(x^2 + y^2 + z^2 = 3 \cdot x \cdot y \cdot z\)

思路:

直接计算等式左右两边的值,比较是否相等即可。注意数据范围在 1000 以内,但乘积可能较大,使用 long long 避免溢出。

代码

点击查看代码
void solve() {
	int x,y,z;
	cin>>x>>y>>z;
	if(x*x+y*y+z*z==3*x*y*z){
		cout<<"Yes\n";;
	}else{
		cout<<"No\n";
	}
}

7-5 吉老师的回归

题意:

给定 N 个题目描述字符串,吉老师按顺序看题,但跳过包含“qiandao”或“easy”(区分大小写)的题目。已知吉老师做完了 M 道题目(实际做的,不包括跳过的),求他当前正在做的题目。如果已做完所有有效题目,输出“Wo AK le”。

思路:

模拟吉老师的做题过程。遍历每个题目,检查是否包含关键词,若包含则跳过。维护一个计数器 cnt 表示有效题目数。当 cnt 等于 M 时,记录下一个有效题目作为答案。遍历结束后,若未记录到答案,则输出“Wo AK le”。

代码

点击查看代码
	int n,m;
	cin>>n>>m;
	string s,t="Wo AK le";
	getline(cin,s);
	int p=0;
	for(int i=1;i<=n;i++){
		getline(cin,s);
		if(s.find("qiandao")!=-1||s.find("easy")!=-1){
			continue;
		}
		if(p==m){
			t=s;
		}
		p++;
	}
	cout<<t<<endl;
}

7-6 大笨钟的心情

题意:

给定 24 个整数,代表一天 24 小时的心情指数。随后输入若干询问,每个询问是一个小时数(0-23),要求输出该小时的心情指数以及是否大于 50(大于则输出 Yes,否则输出 No)。当输入非法小时数(不在 0-23 范围内)时结束输入。

思路:

直接用数组存储 24 个心情指数,对于每个合法询问,输出对应位置的值并判断是否大于 50 即可。

代码

点击查看代码
void solve() {
	for(int i=0;i<24;i++){
		cin>>a[i];
	}
	int x;
	while(cin>>x){
		if(x==-1)break;
		if(x<0||x>23)continue;
		cout<<a[x]<<' ';
		if(a[x]>50){
			cout<<"Yes\n";
		}else{
			cout<<"No\n";
		}
	}
	
}

7-7 爆气球

题意:

给定 \(n\) 个递增的整数坐标和一个长度 \(h\)。孩子跳到一个位置 \(x\),躺平后身体覆盖区间 \([x, x+h]\)。求一个位置 \(x\),使得区间覆盖的气球数最多,输出 \(x\) 和覆盖数。如果有多个 \(x\),输出最小的 \(x\)

思路:

由于坐标有序,使用双指针维护一个滑动窗口,窗口内点的最大差不超过 \(h\)。对于每个窗口,覆盖数为窗口点数,对应的最小 \(x\) 为窗口最大坐标减去 \(h\)。遍历所有窗口,记录最大覆盖数和对应的最小 \(x\)

代码

点击查看代码
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
#define ll long long
const int mod = 998244353;
#define PII pair<int,int>
#define MAXN 100010
#define EXP 20//32
const int INF = 0x3f3f3f3f;

void solve() {
    int n, h;
    cin >> n >> h;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    int mx = 0, ans = 0;
    int j = 0;
    for (int i = 0; i < n; i++) {
        while (j < n && a[j] - a[i] <= h) j++;
        int cnt = j - i;
        if (cnt > mx) {
            mx = cnt;
            ans = a[j - 1] - h;
        } else if (cnt == mx) {
            ans = min(ans, a[j - 1] - h);
        }
    }
    cout << ans << " " << mx << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
}

7-8 帅到没朋友

题意:

给定 N 个朋友圈,每个朋友圈有 K 个人(K=1 表示只有自己)。如果一个人出现在某个 K>1 的朋友圈中,则他有朋友。接下来有 M 个查询,按顺序输出那些没有朋友的人(同一个人只输出一次)。如果没有,输出 "No one is handsome"。

思路:

用数组标记出现在 K>1 朋友圈中的人(有朋友)。查询时,按顺序检查是否被标记过,并且只输出第一次出现的无朋友者。注意输出 ID 为 5 位数字(前补零)。

代码

点击查看代码
int a[N];
int fa[N];
int find(int x){
	if(find(x)==fa[x])return fa[x];
	fa[x]=find(x);
	return fa[x];
}
string ff(int x){
	string s=to_string(x);
	while(s.length()<5)s='0'+s;
	return s;
}
void solve() {
	int n,h;
	cin>>n;
	for(int i=0;i<1e6;i++){
		fa[i]=i;
	}
	for(int i=1;i<=n;i++){
		int k;
		cin>>k;
		int ls;
		for(int j=1;j<=k;j++){
			int x;
			cin>>x;
			if(k>1){
				a[x]=1;
			}
		}
	}
	int m;
	cin>>m;
	int f=1;
	for(int i=1;i<=m;i++){
		int x;
		cin>>x;
		if(a[x]==0){
			if(f){
				f=0;
			}else{
				cout<<' ';
			}
			cout<<ff(x);
			a[x]=1;
		}
	}
	if(f){
		cout<<"No one is handsome";
	}
}

7-9 海盗分赃

题意:

P个海盗偷了D颗钻石,按抽签顺序1到P编号。由1号开始提出分配方案(给出每个海盗具体分到的钻石数),如果得到绝对多数(大于半数)同意,则执行;否则1号被杀死,由2号继续提出方案,直到方案通过或只剩最后一个海盗独占所有钻石。海盗行为准则:

  1. 以自身利益最大化为准;
  2. 在利益相同时,不会故意致同伙于死地(即不会在利益相同时选择杀人);
  3. 分配钻石时,优先分给序号小的海盗。
    给定D和P(3≤P≤D≤100),求1号海盗在自己提出的方案中最多能获得多少钻石。

本题为经典博弈论题目,有兴趣的自行搜索了解。

思路:

逆推法。从最后只剩一个海盗(P号)开始,逐步增加海盗数,计算每个海盗作为提议者时,为让方案通过所需的最小花费(给其他海盗的钻石),从而最大化自身所得。
对于当前提议者i(总人数n = P-i+1),需获得至少need = n/2+1人同意(包括自己)。他从i+1到P号海盗中选择need-1人,用钻石收买。对于海盗j,若当前轮给他x颗钻石,他同意的条件是x > next[j](其中next[j]为下一轮中j能获得的钻石数)。
提议者i会优先选择next[j]最小的海盗,给这些海盗next[j]+1颗钻石以争取同意。计算总花费sum,若sum ≤ D则方案可行,i获得D - sum颗钻石,并更新next数组(被收买的海盗j的next值变为next[j]+1,其余为0);若不可行则i获得0颗且next数组不变(i被杀)。最终next[1]即为答案。

代码

点击查看代码
void solve() {
	int D,P;
	cin>>D>>P;
	vector<int>q;
	q.push_back(D);
	q.push_back(0);
	for(int i=3;i<=P;i++){
		sort(q.begin(),q.end());
		int x=i/2;
		int s=D;
		for(int j=0;j<x;j++){
			q[j]=min(s,q[j]+1);
			s-=q[j];
		}
		for(int j=x;j<q.size();j++){
			q[j]=0;
		}
		q.push_back(s);
//		for(int j=0;j<i;j++){
//			cout<<q[j]<<" \n"[j==i-1];
//		}
	}
	
	cout<<q.back()<<endl;
}

7-10取行李

题意:

给定一个正整数 \(N\) 和一个 \(1\)\(N\) 的重排列,表示旅客的排队顺序。行李按顺序 \(1\)\(N\) 到达传送带窗口,行李 \(i\) 属于旅客 \(i\)。每个人到窗口时,如果行李不是自己的,就走到队尾等待,行李留在窗口。每次检查需要 \(1\) 分钟。求所有行李被取走的总时间和旅客的平均等待时间(从开始到行李被取走的时间)。

思路:

模拟过程。维护一个旅客队列,对于每个行李 \(i\),从队首开始检查,如果不匹配则队首移到队尾,时间加 \(1\);直到匹配,时间加 \(1\) 并取走行李,累加等待时间。总时间即为最后取走行李的时间,平均等待时间为总等待时间除以 \(N\)

代码

点击查看代码
void solve() {
	int n;
	cin>>n;
	queue<int>q;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		q.push(x);
	}
	int s=0,ans=0;
	for(int i=1;i<=n;i++){
		int s1=0;
		while(q.front()!=i){
			s1++;
			q.push(q.front());
			q.pop();
		}
		q.pop();
		s1++;
		s+=s1*(n+1-i);
		ans+=s1;
	}
	cout<<fixed<<setprecision(1)<<ans<<' '<<s*1.0/n<<endl;
}

7-11 德才论

题意:

给定 \(n\) 个考生的准考证号、德分和才分,以及最低分数线 \(l\) 和优先录取线 \(h\)。首先筛选出德分和才分均不低于 \(l\) 的考生,然后根据以下规则分为四类:

  1. 第一类:德分和才分均不低于 \(h\)
  2. 第二类:德分不低于 \(h\),但才分低于 \(h\)(不低于 \(l\))。
  3. 第三类:德分和才分均低于 \(h\),但德分不低于才分。
  4. 其他:剩余达到 \(l\) 的考生。

对每类考生分别按总分降序排序,若总分相同则按德分降序排序,若德分也相同则按准考证号升序排序。输出达标考生总数,并按类别顺序(1→2→3→4)输出所有考生信息。

思路:

直接模拟分类过程,使用四个 vector 分别存储四类考生。读入时根据条件将考生放入对应的容器,然后对每个容器按题目要求排序。最后输出总人数并按顺序输出所有考生。

代码

点击查看代码
struct asd{
	string id;
	int s1,s2;
};
bool cmp(asd x,asd y){
	if(x.s1+x.s2!=y.s1+y.s2)return x.s1+x.s2>y.s1+y.s2;
	if(x.s1!=y.s1)return x.s1>y.s1;
	return x.id<y.id;
}

void solve() {
	int n,l,h;
	cin>>n>>l>>h;
	vector<asd>q1,q2,q3,q4;
	for(int i=1;i<=n;i++){
		asd x;
		cin>>x.id>>x.s1>>x.s2;
		if(x.s1>=h&&x.s2>=h){
			q1.push_back(x);
		}else if(x.s1>=h&&x.s2>=l){
			q2.push_back(x);
		}else if(x.s1>=l&&x.s2>=l&&x.s1>=x.s2){
			q3.push_back(x);
		}else if(x.s1>=l&&x.s2>=l){
			q4.push_back(x);
		}
	}
	sort(q1.begin(),q1.end(),cmp);
	sort(q2.begin(),q2.end(),cmp);
	sort(q3.begin(),q3.end(),cmp);
	sort(q4.begin(),q4.end(),cmp);
	cout<<q1.size()+q2.size()+q3.size()+q4.size()<<endl;
	for(int i=0;i<q1.size();i++){
		cout<<q1[i].id<<' '<<q1[i].s1<<' '<<q1[i].s2<<endl;
	}
	for(int i=0;i<q2.size();i++){
		cout<<q2[i].id<<' '<<q2[i].s1<<' '<<q2[i].s2<<endl;
	}
	for(int i=0;i<q3.size();i++){
		cout<<q3[i].id<<' '<<q3[i].s1<<' '<<q3[i].s2<<endl;
	}
	for(int i=0;i<q4.size();i++){
		cout<<q4[i].id<<' '<<q4[i].s1<<' '<<q4[i].s2<<endl;
	}
}

7-12 红色警报

题意:

给定一个包含 N 个城市和 M 条道路的图,需要按顺序处理 K 个城市被攻占的事件。每次攻占一个城市后,需要判断国家的连通性是否发生变化(即连通分量数量是否增加)。如果增加,发出红色警报;否则只输出普通信息。如果所有城市都被攻占,最后输出"Game Over."。

思路:

使用并查集的思想模拟删除城市后的连通分量数量。每次删除城市后,重新计算剩余城市的连通分量数量,并与之前进行比较。

代码

点击查看代码
vector<int>g[N];
int vis[N];
int vv[N];
int a[N],b[N];
int dfs(int x){
	vis[x]=1;
	int s=vis[x]-vv[x];

	for(auto u:g[x]){
		if(!vis[u]){
			s+=dfs(u);
		}
	}
	return s;
}

void solve() {
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	int s=0;
	for(int i=0;i<n;i++){
		if(!vis[i]){
			a[i]=dfs(i);
			s++;
		}
	}
	int k;
	cin>>k;
	for(int l=1;l<=k;l++){
		int x;
		cin>>x;
		vv[x]=1;
		for(int i=0;i<n;i++){
			vis[i]=vv[i];
			b[i]=0;
		}
		int f=1;
		int ss=0;
		for(int i=0;i<n;i++){
			if(!vis[i]){
				b[i]=dfs(i);
				ss++;
			}
			a[i]=b[i];
		}
		if(s<ss){
			f=0;
		}
		s=ss;
		if(f){
			cout<<"City "<<x<<" is lost.\n";
		}else{
			cout<<"Red Alert: City "<<x<<" is lost!\n";
		}
	}
	int f=0;
	for(int i=0;i<n;i++){
		if(!vv[i]){
			f=1;
		}
	}
	if(!f){
		cout<<"Game Over.\n";
	}
}

7-13 森森美图

题意:

给定一个 \(N \times M\) 的网格,每个格子有一个分值。给定起点 \(A(sx, sy)\) 和终点 \(B(tx, ty)\)。连接 \(A\)\(B\) 的直线将网格分为两部分(不包括直线上的点)。需要在每一部分中分别找一条从 \(A\)\(B\) 的八连通路径,使得两条路径的总得分最小。得分计算规则:

  • 若从 \((x,y)\) 走到正交相邻格(上下左右),得分增加目标格的分值。
  • 若走到斜向相邻格,得分增加目标格分值 \(+\) (当前格分值 \(+\) 目标格分值)\(\times (\sqrt{2}-1)\)
    起点和终点的分值只计算一次(即总得分要减去一次起点和一次终点的分值)。

思路:

使用 Dijkstra 算法求最短路径。分别对直线的两侧进行两次最短路计算,每次只允许访问该侧的点(但允许最后一步走到终点)。判断点 \(P\) 位于直线的哪一侧使用叉积:\(\overrightarrow{AB} \times \overrightarrow{AP}\) 的符号。注意坐标原点在左上角,\(y\) 轴向下为正,叉积公式为 \((tx-sx)*(Y-sy) - (ty-sy)*(X-sx)\)

代码

点击查看代码
int a[N][N];
double mn[N][N];
int vis[N][N];
int xx[4] = {1, 0, -1, 0};
int yy[4] = {0, 1, 0, -1};
int xxx[4] = {1, 1, -1, -1};
int yyy[4] = {-1, 1, -1, 1};
void solve() {
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> a[j][i];
			mn[j][i] = 1e18;
		}
	}
	int sx, sy, tx, ty;
	cin >> sx >> sy >> tx >> ty;
	priority_queue<PIII, vector<PIII>, greater<PIII>>q;
	q.push({a[sx][sy], {sx, sy}});
	mn[sx][sy] = a[sx][sy];
	while (q.size()) {
		auto [s, u] = q.top();
		q.pop();
		auto [x, y] = u;
		if (vis[x][y]) {
			continue;
		}
		vis[x][y] = 1;
		for (int i = 0; i < 4; i++) {
			int X = x + xx[i];
			int Y = y + yy[i];
			if (X < 0 || X >= m || Y < 0 || Y >= n)continue;
			if ((ty - sy) * (X - sx) <= (tx - sx) * (Y - sy)) {
				if (X == tx && Y == ty && (x != sx || y != sy)) {
					double ss = s + a[X][Y];
					if (ss < mn[X][Y]) {
						mn[X][Y] = ss;
					}
				}
				continue;
			}
			double ss = s + a[X][Y];
			if (ss < mn[X][Y]) {
				mn[X][Y] = ss;
				q.push({ss, {X, Y}});
			}
		}
		for (int i = 0; i < 4; i++) {
			int X = x + xxx[i];
			int Y = y + yyy[i];
			if (X < 0 || X >= m || Y < 0 || Y >= n)continue;
			if ((ty - sy) * (X - sx) <= (tx - sx) * (Y - sy)) {
				if (X == tx && Y == ty && (x != sx || y != sy)) {
					double ss = s + a[X][Y] + (a[x][y] + a[X][Y]) * (sqrtl(2) - 1);
					if (ss < mn[X][Y]) {
						mn[X][Y] = ss;
					}
				}
				continue;
			}
			double ss = s + a[X][Y] + (a[x][y] + a[X][Y]) * (sqrtl(2) - 1);
			if (ss < mn[X][Y]) {
				mn[X][Y] = ss;
				q.push({ss, {X, Y}});
			}
		}

	}
	double s1 = mn[tx][ty];
	mn[tx][ty] = 1e18;
	q.push({a[sx][sy], {sx, sy}});
	vis[sx][sy] = 0;
	while (q.size()) {
		auto [s, u] = q.top();
		q.pop();
		auto [x, y] = u;
		if (vis[x][y]) {
			continue;
		}
		vis[x][y] = 1;
		for (int i = 0; i < 4; i++) {
			int X = x + xx[i];
			int Y = y + yy[i];
			if (X < 0 || X >= m || Y < 0 || Y >= n)continue;
			if ((ty - sy) * (X - sx) >= (tx - sx) * (Y - sy)) {
				if (X == tx && Y == ty && (x != sx || y != sy)) {
					double ss = s + a[X][Y];
					if (ss < mn[X][Y]) {
						mn[X][Y] = ss;
					}
				}
				continue;
			}
			double ss = s + a[X][Y];
			if (ss < mn[X][Y]) {
				mn[X][Y] = ss;
				q.push({ss, {X, Y}});
			}
		}
		for (int i = 0; i < 4; i++) {
			int X = x + xxx[i];
			int Y = y + yyy[i];
			if (X < 0 || X >= m || Y < 0 || Y >= n)continue;
			if ((ty - sy) * (X - sx) >= (tx - sx) * (Y - sy)) {
				if (X == tx && Y == ty && (x != sx || y != sy)) {
					double ss = s + a[X][Y] + (a[x][y] + a[X][Y]) * (sqrtl(2) - 1);
					if (ss < mn[X][Y]) {
						mn[X][Y] = ss;
					}
				}
				continue;
			}
			double ss = s + a[X][Y] + (a[x][y] + a[X][Y]) * (sqrtl(2) - 1);
			if (ss < mn[X][Y]) {
				mn[X][Y] = ss;
				q.push({ss, {X, Y}});
			}
		}

	}
	double s2 = mn[tx][ty];
//	cout << s1 << ' ' << s2 << endl;
	cout << fixed << setprecision(2) << s1 + s2 - a[sx][sy] - a[tx][ty] << endl;
}

7-14 那就别担心了

题意:

给定一个有向无环图,问从起点A到终点B有多少条不同的路径,并判断是否所有从A出发的路径都能到达B。

思路:

  1. 用DFS标记从A可达的所有节点
  2. 统计可达节点的出度和入度(仅考虑可达节点之间的边)
  3. 计数:用拓扑排序+DP计算从A到B的路径数,dp[A]=1,按拓扑序累加
  4. 判断逻辑自洽:检查所有可达节点中,除了B以外是否有出度为0的节点

代码

点击查看代码
int a[N];
vector<int>g[N];
int vis[N];
int d[N];
int r[N];
int A,B;

void dfs(int x){
	vis[x]=1;
	if(x==B)return;
	for(auto u:g[x]){
		d[u]++;
		r[x]++;
		if(!vis[u]){
			dfs(u);
		}
	}
}

void solve() {
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		g[x].push_back(y);
	}
	cin>>A>>B;
	dfs(A);
	queue<int>q;
	a[A]=1;
	vector<int>ss;
	for(int i=1;i<=n;i++){
		if(vis[i]&&r[i]==0){
			ss.push_back(i);
//			cout<<i<<endl;
		}
	}
	q.push(A);
	int f=0;
	if(ss.size()==1&&ss[0]==B){
		f=1;
	}
	while(q.size()){
		auto x=q.front();
		q.pop();
		for(auto u:g[x]){
			a[u]+=a[x];
			d[u]--;
			if(d[u]==0){
				q.push(u);
			}
		}
	}
	cout<<a[B]<<" ";
	if(f)cout<<"Yes\n";
	else cout<<"No\n";
}

7-15 超能力者大赛

题意:

模拟主角按照贪心算法行动,直到击败所有对手、无法击败任何对手或时间结束。

思路:

按照题目描述的贪心算法模拟即可。需要维护每个城市中的对手集合,使用 multiset 存储能力值。每次选择目标时,遍历所有城市,找到能力值不超过主角且最大的对手,按距离、途径城市数、城市编号排序。移动需要计算最短路径,用 Floyd 预处理。战斗后合并城市中能力值不超过主角的对手。注意日期计算和边界条件。本题细节较多,详细细节见代码。

代码

点击查看代码
int a[N];
int p[N];
multiset<int>g[M];
int mn[M][M];
int mn1[M][M];
void solve() {
	int n, m, k, D;
	cin >> n >> m >> k >> D;
	set<PII>q;
	for (int i = 0; i < n; i++) {
		cin >> p[i] >> a[i];
		if (i)g[p[i]].insert(a[i]), q.insert({a[i], i});
	}
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < m; j++) {
			if (i != j)mn1[i][j] = mn[i][j] = 1e18;
		}
	}
	for (int i = 1; i <= k; i++) {
		int x, y, w;
		cin >> x >> y >> w;
		mn[y][x] = mn[x][y] = w;
		mn1[y][x] = mn1[x][y] = 1;
	}
	for (int k = 0; k < m; k++) {
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < m; j++) {
				if (mn[i][k] + mn[k][j] < mn[i][j]) {
					mn[i][j] = mn[i][k] + mn[k][j];
					mn1[i][j] = mn1[i][k] + mn1[k][j];
				} else if (mn[i][k] + mn[k][j] == mn[i][j] && mn1[i][k] + mn1[k][j] < mn1[i][j]) {
					mn1[i][j] = mn1[i][k] + mn1[k][j];
				}
			}
		}
	}
	int d = 1;
	int lst = -1;
	while (d <= D) {
		int f1 = 1;
		for (int i = 0; i < m; i++) {
			if (g[i].size())f1 = 0;
		}
		if (f1) {
			cout << "WIN on day " << d << " with " << a[0] << "!" << endl;
			return;
		}
		int f2 = 1;
		for (int i = 0; i < m; i++) {
			if (i == lst) continue;
			if (g[i].size()) {
				auto it = g[i].upper_bound(a[0]);
				if (it != g[i].begin()) f2 = 0;
			}
		}
		if (f2) {
			cout << "Lose on day " << d << " with " << a[0] << "." << endl;
			return;
		}
		int ans = -1;
		int mi = -1;
		for (int i = 0; i < m; i++) {
			if (i == lst) continue;
			if (g[i].empty()) continue;
			if (mn[p[0]][i] >= 1e18) continue;
			auto it = g[i].upper_bound(a[0]);
			if (it == g[i].begin()) continue;
			int v = *prev(it);
			if (mi == -1 || v > ans || (v == ans && mn[p[0]][i] < mn[p[0]][mi]) || (v == ans && mn[p[0]][i] == mn[p[0]][mi] && mn1[p[0]][i] < mn1[p[0]][mi]) || (v == ans && mn[p[0]][i] == mn[p[0]][mi] && mn1[p[0]][i] == mn1[p[0]][mi] && i < mi)) {
				ans = v;
				mi = i;
			}
		}
		if (mi == -1) {
			cout << "Lose on day " << d << " with " << a[0] << "." << endl;
			return;
		}
		lst = mi;
		d += mn[p[0]][mi];
		if (d-1 > D) {
			break;
		}
		if (p[0] != mi) {
			cout << "Move from " << p[0] << " to " << mi << "." << endl;
			p[0] = mi;
		}
		if (d > D) {
			break;
		}
		cout << "Get " << ans << " at " << mi << " on day " << d << "." << endl;
		g[mi].erase(g[mi].find(ans));
		a[0] += ans;
		int sum = 0;
		while (g[mi].size() && *g[mi].begin() <= a[0]) {
			sum += *g[mi].begin();
			g[mi].erase(g[mi].begin());
		}
		if (sum > 0) {
			g[mi].insert(sum);
		}
		f1 = 1;
		for (int i = 0; i < m; i++) {
			if (g[i].size())f1 = 0;
		}
		if (f1) {
			cout << "WIN on day " << d << " with " << a[0] << "!" << endl;
			return;
		}
		d++;
		if (d > D) {
			break;
		}
		if (sum > 0 && sum <= a[0] && g[mi].size() == 1) {
			cout << "Get " << sum << " at " << mi << " on day " << d << "." << endl;
			g[mi].erase(g[mi].find(sum));
			a[0] += sum;
			f1 = 1;
			for (int i = 0; i < m; i++) {
				if (g[i].size())f1 = 0;
			}
			if (f1) {
				cout << "WIN on day " << d << " with " << a[0] << "!" << endl;
				return;
			}
			d++;
			if (d > D) {
				break;
			}
		}
	}
	cout << "Game over with " << a[0] << "." << endl;
}
posted @ 2026-03-24 16:04  ptlks  阅读(236)  评论(0)    收藏  举报