2025/10/02皇后游戏

题解:P2123 皇后游戏

题目分析

这道题是 NOIP 2012 国王游戏问题的变种。题目要求我们重新排列大臣的顺序,使得获得奖金最多的大臣所获奖金尽可能少。

奖金计算规则

设第 \(i\) 位大臣左手上的数为 \(a_i\),右手上的数为 \(b_i\),则:

  • \(1\) 位大臣的奖金:\(c_1 = a_1 + b_1\)
  • \(i\) 位大臣的奖金(\(i \geq 2\)):\(c_i = \max(c_{i-1}, \sum_{j=1}^{i} a_j) + b_i\)

解题思路

关键观察

与国王游戏类似,我们需要找到一个合适的大臣排列顺序。通过分析可以发现,大臣的排列顺序会影响最终的奖金最大值。

排序策略

代码中的排序规则是核心:

bool cmp(node a, node b) {
    if(a.x <= a.y && a.x <= b.x || b.x <= a.x && b.x <= b.y) 
        return a.x != b.x ? a.x < b.x : a.y > b.y;
    return a.y != b.y ? a.y > b.y : a.x < b.x;
}

这个比较函数的意思是:

  1. 如果大臣A满足 \(a_x \leq a_y\)\(a_x \leq b_x\),或者大臣B满足 \(b_x \leq a_x\)\(b_x \leq b_y\),那么:
    • 优先按 \(x\) 升序排列
    • \(x\) 相同时按 \(y\) 降序排列
  2. 否则:
    • 优先按 \(y\) 降序排列
    • \(y\) 相同时按 \(x\) 升序排列

算法流程

  1. 读取输入数据
  2. 按照上述规则对大臣进行排序
  3. 计算排序后的奖金序列:
    • 维护前缀和 l 表示当前已处理大臣的左手数值之和
    • 维护当前最大值 ans
    • 对于每个大臣,更新前缀和后计算其奖金

代码实现

#include<bits/stdc++.h>
using namespace std;

int T = 1, n;
long long l, ans;

struct node {
    int x, y, id;
} a[20005];

bool cmp(node a, node b) {
    if(a.x <= a.y && a.x <= b.x || b.x <= a.x && b.x <= b.y) 
        return a.x != b.x ? a.x < b.x : a.y > b.y;
    return a.y != b.y ? a.y > b.y : a.x < b.x;
}

int main() {
    // cin >> T;
    while(T--) {
        l = 0; ans = 0;
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i].x >> a[i].y;
            a[i].id = i;
        }
        
        sort(a + 1, a + n + 1, cmp);
        
        for(int i = 1; i <= n; i++) {
            l += a[i].x;
            if(ans < l) 
                ans = l + a[i].y;
            else 
                ans += a[i].y;
        }
        
        cout << ans << "\n";
        for(int i = 1; i <= n; i++) 
            cout << a[i].id << " ";
    }
    return 0;
}

示例分析

样例1

输入:

1
3
4 1
2 2
1 2

排序后的大臣顺序为:3号(1,2)、2号(2,2)、1号(4,1)

计算过程:

  • 大臣3:奖金 = 1 + 2 = 3
  • 大臣2:奖金 = max(3, 1+2) + 2 = max(3,3) + 2 = 5
  • 大臣1:奖金 = max(5, 1+2+4) + 1 = max(5,7) + 1 = 8

输出:8

复杂度分析

  • 时间复杂度:\(O(T \cdot n \log n)\),主要来自排序操作
  • 空间复杂度:\(O(n)\),用于存储大臣信息

总结

这道题的关键在于找到合适的大臣排列顺序。通过特定的排序规则,我们可以最小化奖金的最大值。排序策略综合考虑了大臣左右手数值的关系,优先处理那些"左手数值较小"或者"右手数值较大"的大臣,从而平衡奖金分配。

这种贪心策略的题目需要仔细分析问题特性,找到合适的比较规则,是算法竞赛中的经典题型。

#include<bits/stdc++.h>
using namespace std;
int T,n;
long long l,ans;
struct node{
	int x,y,id;
}a[20005];
bool cmp(node a,node b)
{
	if(a.x<=a.y&&a.x<=b.x||b.x<=a.x&&b.x<=b.y) return a.x!=b.x?a.x<b.x:a.y>b.y;
	return a.y!=b.y?a.y>b.y:a.x<b.x;
}
int main()
{
	cin>>T;
	while(T--)
	{
		l=0;ans=0;
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].x>>a[i].y;
			a[i].id=i;
		}
		sort(a+1,a+n+1,cmp);
		for(int i=1;i<=n;i++)
		{
			l+=a[i].x;
			if(ans<l) ans=l+a[i].y;
			else ans+=a[i].y;
		}
		cout<<ans<<"\n";
		for(int i=1;i<=n;i++) cout<<a[i].id<<" ";
	}
	return 0;
}
posted @ 2025-10-02 21:42  cdqz_hfu_hzb  阅读(12)  评论(0)    收藏  举报