[Codeforces #608 div2]1271D Portals
Description
You play a strategic video game (yeah, we ran out of good problem legends). In this game you control a large army, and your goal is to conquer nnn castles of your opponent.
Let’s describe the game process in detail. Initially you control an army of kkk warriors. Your enemy controls n castles; to conquer the iii-th castle, you need at least ai warriors (you are so good at this game that you don’t lose any warriors while taking over a castle, so your army stays the same after the fight). After you take control over a castle, you recruit new warriors into your army — formally, after you capture the iii-th castle, bib_ibi warriors join your army. Furthermore, after capturing a castle (or later) you can defend it: if you leave at least one warrior in a castle, this castle is considered defended. Each castle has an importance parameter cic_ici, and your total score is the sum of importance values over all defended castles. There are two ways to defend a castle:
if you are currently in the castle iii, you may leave one warrior to defend castle iii;
there are mmm one-way portals connecting the castles. Each portal is characterised by two numbers of castles uuu and vvv (for each portal holds u>vu>vu>v). A portal can be used as follows: if you are currently in the castle u, you may send one warrior to defend castle vvv.
Obviously, when you order your warrior to defend some castle, he leaves your army.
You capture the castles in fixed order: you have to capture the first one, then the second one, and so on. After you capture the castle iii
(but only before capturing castle i+1) you may recruit new warriors from castle iii, leave a warrior to defend castle iii, and use any number of portals leading from castle iii to other castles having smaller numbers. As soon as you capture the next castle, these actions for castle iii won’t be available to you.
If, during some moment in the game, you don’t have enough warriors to capture the next castle, you lose. Your goal is to maximize the sum of importance values over all defended castles (note that you may hire new warriors in the last castle, defend it and use portals leading from it even after you capture it — your score will be calculated afterwards).
Can you determine an optimal strategy of capturing and defending the castles?
Input
The first line contains three integers n,mandk(1≤n≤5000,0≤m≤min(n(n−1)2,3⋅105),0≤k≤5000n, m and k (1≤n≤5000, 0≤m≤min(\frac{n(n−1)}{2},3⋅10^5), 0≤k≤5000n,mandk(1≤n≤5000,0≤m≤min(2n(n−1),3⋅105),0≤k≤5000) — the number of castles, the number of portals and initial size of your army, respectively.
Then nnn lines follow. The iii-th line describes the iii-th castle with three integers aia_iai, bib_ibi and ci(0≤ai,bi,ci≤5000)c_i (0≤a_i,b_i,c_i≤5000)ci(0≤ai,bi,ci≤5000) — the number of warriors required to capture the iii -th castle, the number of warriors available for hire in this castle and its importance value.
Then mmm lines follow. The iii-th line describes the iii-th portal with two integers uiu_iui and vi(1≤vi<ui≤n)v_i (1≤v_i<u_i≤n)vi(1≤vi<ui≤n), meaning that the portal leads from the castle uiu_iui to the castle viv_ivi. There are no two same portals listed.
It is guaranteed that the size of your army won’t exceed 5000 under any circumstances (i. e. k+∑i=1nbi≤5000k+∑_{i=1}^{n}b_i≤5000k+∑i=1nbi≤5000).
Output
If it’s impossible to capture all the castles, print one integer −1−1−1.
Otherwise, print one integer equal to the maximum sum of importance values of defended castles.
题意
按顺序占领1−n1-n1−n所有城堡,可以选择在该城堡刚被占领时留下一个士兵获得权值,也可以通过u,vu,vu,v单向边来在后面占领该城堡获得权值。
思路
update at 19/12/19
之前的做法过于混乱(甚至我自己都觉得像是瞎蒙的),因此借鉴了其他神犇的题解仔细思考后发现可以使用反悔型贪心来做这道题。
思路为:
一路扫过去,每次都假定留守所有可留守的点,发现当前的士兵不够则从已留守的点中选取权值最小的放弃留守。
依然是基于 靠后留守更优 策略,预处理出每个点可留守的集合,遍历到该点直接将可留守的全部push到已留守集合中,再遍历下一个点。 在遍历点过程中发现士兵不够,pop已留守集合,如果pop空了还不够,说明游戏失败。
代码一起放到后面了。自我感觉注释够详细了。
能不能占领完城堡很好判断,只需要每次都取光士兵而不留守扫一遍,如果这种情况下都有无法占领的城堡,那一定没法占领完。
如果要留守城堡iii,那么一定是在最后留守最好。
因此可以开一个lastlastlast数组,last[i]last[i]last[i]标记要留守iii的最晚位置是哪里。
现在开始思考留守的实质:如果在位置iii留守,就是使区间[last[i]+1,n][last[i]+1,n][last[i]+1,n]的可用士兵数量减少111。
我们要保证这段位置的可用士兵数量减少111之后还能占领所有的城堡。
于是有:
inline bool check(const int &p)
{
for(int i = p;i<=n;++i)
if(a[i] + 1 > d[i])//d[i]代表占领i前的可用士兵数量
return false;
return true;
}
随后思考要怎么选择留守哪些城堡。可以发现,贪心地留守权值大的城堡一定是最优的。
因为留守一个城堡影响的是[last[i]+1,n][last[i]+1,n][last[i]+1,n]整个区间,我们可以讨论:
如果c[i1]>c[i2]c[i1] > c[i2]c[i1]>c[i2],且last[i1]<last[i2]last[i1] < last[i2]last[i1]<last[i2],即i1i1i1的权值比i2i2i2大,有3种情况:
1.[last[i1]+1,n]的区间全部减去2个士兵后依然能占领所有城堡。那么就是i1i1i1和i2i2i2都留守的情况,此时操作顺序不会影响结果。
2.[last[i1]+1,last[i2]]的区间减去1个士兵能满足,[last[i2]+1,n]减去2个士兵能满足,由于我们先处理权值大的,处理完i1i1i1后还能处理i2i2i2,也可以得到正确答案。
3.[last[i1]+1,n]的区间减去1个士兵能满足,此时i1i1i1与i2i2i2只能选择一个,那么肯定是选取i1i1i1好。
选取i1i1i1和i2i2i2的区别就在于[last[i1]+1,last[i2]][last[i1]+1,last[i2]][last[i1]+1,last[i2]]这段区间,是否会对结果造成影响呢?
我们将选择i1i1i1看做消耗[last[i1]+1,last[i2]][last[i1]+1,last[i2]][last[i1]+1,last[i2]]和[last[i2]+1,n][last[i2]+1,n][last[i2]+1,n]内的士兵数量。
假定有一个i3i3i3的选择,,且c[i3]<c[i1]c[i3] < c[i1]c[i3]<c[i1],如果last[i3]>last[i2]last[i3] > last[i2]last[i3]>last[i2],很显然选择i1i1i1和i2i2i2是等效的。
如果last[i3]<last[i1]last[i3] < last[i1]last[i3]<last[i1],那么选取i3i3i3的代价比选取i1i1i1更大且得利更少,因此此时先选择i1i1i1可以保证整体更优。
如果last[i3]在[last[i1+1],last[i2]]last[i3]在[last[i1+1],last[i2]]last[i3]在[last[i1+1],last[i2]]之间,若选i1i1i1后还能选i3i3i3则肯定选i1i1i1,若选i1i1i1后不能选i3i3i3,那么选i3i3i3后肯定也不能选i1i1i1,但c[i3]<c[i1]c[i3] < c[i1]c[i3]<c[i1],因此可能情况下还是选i1i1i1更好。
另外,若c[i1]>c[i2]c[i1] > c[i2]c[i1]>c[i2]且last[i1]>last[i2]last[i1] > last[i2]last[i1]>last[i2],很显然此时选择i1比选择i2好。
可能写的有点乱……
Code
#include <cstdio>
#include <algorithm>
template <typename T> inline T max(const T &a,const T &b){return a>b?a:b;}
inline char nc()
{
static char buf[1000000],*p1 = buf,*p2 = buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
void read(int &r)
{
static char c;r=0;
for(c=nc();c>'9'||c<'0';c=nc());
for(;c>='0'&&c<='9';r=(r<<1)+(r<<3)+(c^48),c=nc());
}
int n,m,k;
int last[5001];//last[i]保存i点最晚能在哪里被派兵守卫
int a[5001],b[5001],c[5001],d[5001];//d[i]代表占领i前到达i这里的兵力
int q[5001];
bool cmp(const int &a,const int &b)
{
return c[a] > c[b];
}
inline bool check(const int &p)
{
for(int i = p;i<=n;++i)
if(a[i] + 1 > d[i])
return false;
return true;
}
int main()
{
read(n);
read(m);
read(k);
int now = k;//现在兵力
for(int i = 1;i<=n;++i)
{
read(a[i]);
read(b[i]);
read(c[i]);
d[i] = now;
if(now < a[i])
{
printf("-1");
return 0;
}
now += b[i];//顺便判断是否能占领所有城堡,也就是判断每次取完兵能否一路全部占领
q[i] = last[i] = i;
}
int u,v;
for(;m;--m)
{
read(u);
read(v);
last[v] = max(last[v],u);
}
std::sort(q+1,q+1+n,cmp);//按照贡献值排序
int ans = 0;
for(int i = 1;i<=n;++i)
{
if(check(last[q[i]] + 1))
{
for(int j = last[q[i]] + 1;j<=n;++j)
--d[j];
ans += c[q[i]];
}
}
printf("%d",ans);
return 0;
}
这里是反悔贪心的代码:
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
//读入优化
inline char nc()
{
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
template <typename T>
inline void read(T &r)
{
static char c;r = 0;
for (c = nc(); c > '9' || c < '0'; c = nc());
for (; c >= '0' && c <= '9'; r = (r << 1) + (r << 3) + (c ^ 48), c = nc());
}
const int maxn = 5e3 + 10;
int n, m, k, ans;
int a[maxn], b[maxn], c[maxn];
int last[maxn]; //last[i]记录i最后能在哪里被留守
priority_queue<int, vector<int>, greater<int>> q;
vector<int> defback[maxn]; //存储该点可以向前留守什么点
int main()
{
read(n);
read(m);
read(k);
for (int i = 1; i <= n; ++i)
{
read(a[i]);
read(b[i]);
read(c[i]);
last[i] = i; //初始化
}
int u, v;
for (; m; --m)
{
read(u);
read(v);
last[v] = last[v] > u ? last[v] : u; //取更大者,因为越后留守一定越好
}
for (int i = 1; i <= n; ++i)
defback[last[i]].push_back(i);
for (int i = 1; i <= n; ++i)
{
while (k < a[i] && !q.empty()) //不够的话,从已留守的城堡中取出权值最小的放弃,即反悔
++k, q.pop();
//defended[q.top()] = false; 事实上并不需要对是否已经留守做标记,因为所有点最多会被判断留守一次,也就是最多被标true false一次
if (k < a[i]) //如果取完还是不够,那没办法了,输出-1
goto fail;
//占领当前点
k += b[i];
//往前留守,这里不用考虑当前点,因为当前点要么也在vector中,要么在后面可以被留守
for (vector<int>::iterator it = defback[i].begin(); it != defback[i].end(); ++it)
--k, q.push(c[*it]); //可以直接全部push到留守集合中,等后面再反悔,不用考虑会不会让k<0
}
//最后一步后还要判断k是否大于0进行一次反悔
while (k < 0 && !q.empty())
++k, q.pop();
if (k < 0)
goto fail;
while (!q.empty())//统计已留守点的权值和
ans += q.top(), q.pop();
printf("%d", ans);
return 0;
fail:
puts("-1");
return 0;
}
[Codeforces #608 div2]1271D Portals的更多相关文章
- [Codeforces #608 div2]1271C Shawarma Tent
Description The map of the capital of Berland can be viewed on the infinite coordinate plane. Each p ...
- [Codeforces #608 div2]1272B Blocks
Description There are nnn blocks arranged in a row and numbered from left to right, starting from on ...
- [Codeforces #608 div2]1271A Suits
Description A new delivery of clothing has arrived today to the clothing store. This delivery consis ...
- Codeforces #180 div2 C Parity Game
// Codeforces #180 div2 C Parity Game // // 这个问题的意思被摄物体没有解释 // // 这个主题是如此的狠一点(对我来说,),不多说了这 // // 解决问 ...
- Codeforces #541 (Div2) - E. String Multiplication(动态规划)
Problem Codeforces #541 (Div2) - E. String Multiplication Time Limit: 2000 mSec Problem Descriptio ...
- Codeforces #541 (Div2) - F. Asya And Kittens(并查集+链表)
Problem Codeforces #541 (Div2) - F. Asya And Kittens Time Limit: 2000 mSec Problem Description Inp ...
- Codeforces #541 (Div2) - D. Gourmet choice(拓扑排序+并查集)
Problem Codeforces #541 (Div2) - D. Gourmet choice Time Limit: 2000 mSec Problem Description Input ...
- Codeforces #548 (Div2) - D.Steps to One(概率dp+数论)
Problem Codeforces #548 (Div2) - D.Steps to One Time Limit: 2000 mSec Problem Description Input Th ...
- 【Codeforces #312 div2 A】Lala Land and Apple Trees
# [Codeforces #312 div2 A]Lala Land and Apple Trees 首先,此题的大意是在一条坐标轴上,有\(n\)个点,每个点的权值为\(a_{i}\),第一次从原 ...
随机推荐
- VScode使用入门
使用命令行打开 vscode 如何获取code命令 一般来说,当windows安装了vscode之后,重启之后就可以使用 code 这个命令 也可以直接将vscode直接加入到环境变量中进行使用 命令 ...
- 转入软工后第一节java课的作业
这个作业,鸽了好久.本来大家都在中秋前发了,我摸摸索索加上各种缓慢的学习,终于是将他做完了. 做完之后,java最基本的输入输出功能都基本学习到了.下面附上代码: import java.util.* ...
- RFID学习
RFID:射频识别卡有读卡器和电子标签(有源/无源/半有源)组成,工作频率有LF/HF/UHF/MF,低频的通讯距离近(1m)常用来做门禁:高频的通讯距离远可以批量读取. 无源的轻巧/防拆一致性好,在 ...
- BIND DNS配置!
1.RPM 包的主要作用bind:提供了域名服务器的主要程序及相关文件bind-utils:提供了对 DNS 服务器的测试工具程序,如 nslookup 等bind-libs:提供了 bind.bin ...
- windows之杀死端口
提示: 查询端口 查询pid 杀死任务 通过 cmd 进入控制台: 查询端口: netstat -aon|findstr 1111 查询pid下的任务: tasklist|findstr 6616 杀 ...
- LeetCode 79.单词搜索 - JavaScript
题目描述:给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中"相邻"单元格是那些水平相邻或垂直相邻的单元格.同一个单 ...
- 解决IDEA部署web项目时,jar包拷贝不全的问题
原因 先前已部署过,输出目录有lib文件夹. 再次部署时,IDEA一检测,发现输出目录已经存在lib文件夹,认为已经拷贝过了,为节省时间,不再重新拷贝jar包,殊不知我们新添加了jar包. 于是我们新 ...
- 看完这篇微服务架构设计思想,90%的Java程序员都收藏了
本博客强烈推荐: Java电子书高清PDF集合免费下载 https://www.cnblogs.com/yuxiang1/p/12099324.html 微服务 软件架构是一个包含各种组织的系统组织, ...
- C++ STL之动态数组vector(⽮量)的使⽤
写再最前面:摘录于柳神的笔记: 之前C语⾔⾥⾯⽤ int arr[] 定义数组,它的缺点是数组的⻓度不能随⼼所欲的改变,⽽C++⾥⾯有⼀个能完全替代数组的动态数组 vector (有的书⾥⾯把它翻 ...
- c++ 读取、保存单张图片
转载:https://www.jb51.net/article/147896.htm 实际上就是以二进制形式打开文件,将数据保存到内存,在以二进制形式输出到指定文件.因此对于有图片的文件,也可以用这种 ...