CF1271D 城堡
1 CF1271D 城堡
2 题目描述
时间限制 \(2s\) | 空间限制 \(256M\)
你在玩一个策略游戏(好吧,好的传说故事编不出来了),在这个游戏中你控制一个军队,你的目标是征服你对面的 \(n\) 个城堡。游戏的过程是这样的,开始的时候你控制 \(k\) 个战士的军队,你的敌人控制 \(n\) 个城堡,要征服第 \(i\) 个城堡,你需要至少 \(a_i\) 个战士(假设你的战士在征服城堡的过程中不会死亡,这样征服后你的战士人数不变)。每当你征服一个城堡后你都会给你的军队招募新兵,每当你征服第 \(i\) 个城堡,你招募到 \(b_i\) 个新战士。每当征服一个城堡后你可以占领它,至少留一个战士驻守在城堡里面你就占领了这个城堡。每个城堡都有个重要性值 \(c_i\),你的总分是所有占领城堡的重要性值的累加和。你有两个方法来占领城堡:
- 如果你现在在城堡 \(i\) 里面,你可以留下一个战士来驻守这个城堡 \(i\)。
- 有 \(m\) 个单向道路连接到城堡,每个道路用两个城堡的两个数字 \(u\) 和 \(v\) 表示,每个 \(u > v\)。通道可以这么使用,如果你在城堡 \(u\) 里面,你可以送一个战士去驻守城堡 \(v\)。
很明显当你命令你的战士去驻守城堡的时候,他离开了你的军队。攻占堡垒是用一个固定的顺序,你必须攻占第一个,然后下一个,然后继续。当你占领了城堡 \(i\) 但是还没有占领 \(i+1\) 之前,你可以从城堡 \(i\) 里面招募新的战士,留下一个战士驻守在城堡 \(i\) 里面。然后使用任何连接城堡 \(i\) 到其他更小数字的城堡。一旦你征服了下一个城堡,你就不能再使用城堡 \(i\) 的资源。如果在游戏过程中你没有足够的战士去征服下一个城堡了,你就输了。你的目标是征服城堡的重要性值的累加和最大。注意你征服的最后一个城堡后你还是可以招募新战士,驻守城堡,从某一条道路离开,这样这个城堡的得分才会计算进去。
请找出征服城堡的最佳策略。
数据范围:\(1 ≤ 𝑛 ≤ 5000, 0 ≤ 𝑚 ≤ min(\frac{𝑛(𝑛−1)}{2},3\times 10^5),0 ≤ 𝑘 ≤ 5000)\)。\(n, m, k\) 分别表示城堡的个数,道路的个数和你开始的时候战士的数量。
3 题解
首先我们观察到,\(n \le 5000\),也就是说时间复杂度是 \(O(n^2)\) 左右。我们接下来分析题目中蕴藏的贪心性质:
\(1.\) 如果我们要派兵驻守一些城堡,那么我们肯定先驻守重要性高的城堡,再驻守重要性低的城堡。这是因为,驻守重要性高的城堡也只需要花费一位士兵,不存在驻守重要性高的城堡后导致本来可以驻守的超过一个的重要性较低的城堡无法被驻守。
$2. $ 如果我们要驻守一个城堡,那么我们一定是要从与当前这个城堡连接的编号最大的城堡派兵驻守。这是因为编号越大,当前的城堡减小人数后影响到的城堡数也就越少。而且由于每次减少一个人后随后到达所有城堡时的总人数都减少且仅减少 \(1\),所以编号大的城堡减少人所影响到的城堡一定会被编号小的城堡减少人所影响,且影响程度一致。
由此,我们可以想出做法:
一开始在不派兵驻守的情况下依次占领所有城市,判断是否无法赢得游戏。如果可以获胜,那么我们按重要性从大到小枚举每一个城堡。对于当前城堡,我们有两种驻兵方式:第一次来到该城堡时就派兵驻守,或者在占领了编号更大的城堡后反过来往当前城堡派兵。对于第一种情况,我们只需要重新占领一遍所有城堡,注意在攻占到这个城堡时将总兵数 \(-1\),此时如果有一刻的总兵力不足让我们占领下一个城堡,那么我们就无法通过第一种情况驻守当前城堡。对于第二种情况,我们利用第二个贪心策略,从 \(n\) 到当前节点 \(+1\) 枚举向当前城堡派兵的城堡 \(j\),如果当前节点与 \(j\) 之间没有连边那么直接 \(continue\),否则重新占领一遍所有城堡,在攻占到 \(j\) 时将总兵数 \(-1\)。如果有一刻的总兵力不足让我们占领下一个城堡,那么我们就无法通过第二种情况驻守当前城堡。两种情况都无法驻守的城堡便无法被我们驻守。否则只要有一种情况满足,那么答案就加上我们当前城堡的重要性。
这里为了便于查询所有通往某一个城堡的那些城堡,我们利用邻接矩阵存储(\(n\) 是 \(5000\) 所以当前这个邻接矩阵空间不会溢出)。当然也有一个小 \(trick\) 可以帮助我们用邻接表存储:将所有的边都反过来存储。这样一来查询通往某一个城堡的那些城堡就变成了查询某一个城堡通往的那些城堡。
4 代码(空格警告):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5005;
int n, m, k, tot, u, v, maxn, now, ans;
int a[N], b[N], c[N], c2[N];
bool flag, Map[N][N];
bool cmp(int x, int y)
{
return c[x] > c[y];
}
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i], c2[i] = i;
sort(c2+1, c2+n+1, cmp);
for (int i = 1; i <= m; i++)
{
cin >> u >> v;
Map[u][v] = 1;
}
now = k;
for (int i = 1; i <= n; i++)
{
if (now < a[i])
{
cout << -1;
return 0;
}
now += b[i];
}
for (int i = 1; i <= n; i++)
{
maxn = c2[i];
now = k;
flag = 1;
for (int j = n; j >= 1; j--)
{
if (!Map[j][maxn]) continue;
flag = 0;
now = k;
for (int l = 1; l <= n; l++)
{
if (now < a[l])
{
flag = 1;
break;
}
if (l == j) now--;
now += b[l];
}
if (!flag && now >= 0)
{
ans += c[maxn];
b[j]--;
break;
}
}
if (!flag) continue;
flag = 0;
now = k;
for (int j = 1; j <= n; j++)
{
if (now < a[j])
{
flag = 1;
break;
}
if (j == maxn) now--;
now += b[j];
}
if (!flag && now >= 0)
{
ans += c[maxn];
b[maxn]--;
continue;
}
}
cout << ans;
return 0;
}