codeforces1271D 2100分贪心

题目传送门

题意:

n个城堡,m条单向边,单向边的方向永远是下标大的城堡到下标小的城堡。

初始时你的军队有k个士兵。

现在你依次占领n个城堡,占领顺序是先占领下标小的城堡,再占领下标大的城堡。

占领第 \dpi{150}i 个城堡,需要拥有 \dpi{150}a_i 个士兵,但是并不消耗士兵。占领第 \dpi{150}i 个城堡后,你的军队会新增 b_i 个士兵。

保卫第 \dpi{150}i 个城堡会获得 c_i 个金币。

保卫一个城堡需要把一个士兵留在该城堡。

军队的移动顺序是下标小的城堡到下标大的城堡,不会反向移动。

当你在第 \dpi{150}i 个城堡时,你可以派一个士兵保卫第 \dpi{150}i 个城堡。

当你在第 \dpi{150}i 个城堡时,你可以派一个士兵保卫第 j 个城堡。要求必须存在第 \dpi{150}i 个城堡到第 j 个城堡的单向边。

一个士兵保卫城堡就会离开军队。

如果不能占领所有城堡,那就输出-1。

问你在占领所有城堡后最多能获得多少个金币。

数据范围:1\leqslant n\leqslant 5000 , 0\leqslant m\leqslant min(\frac{n(n-1)}{2},3\cdot 10^5),0\leqslant k\leqslant 5000

0 \leqslant a_i , b_i , c_i \leqslant 5000 , k + \sum_{i=1}^{k}b_i \leqslant 5000

题解:

需要观察到2个切入点才能解决。

切入点1:如果要把一个士兵安排到第 \dpi{150}i 个城堡,那么一定要保证能占领后面所有的城堡。

切入点2:如果要把一个士兵安排到第 \dpi{150}i 个城堡,那么一定是合法的最靠后的城堡向第 \dpi{150}i 个城堡派士兵。

因此,每个城堡都对应一个可向自身派遣士兵的最靠后的城堡。

依次保卫金币数最多的城堡即可。保卫该城堡后,要满足可以占领所有城堡。

感受:

set用错,直接调了两个小时bug。

欲哭无泪。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 5005 ;
const int maxm = 1e6 + 5 ;
const ll mod = 998244353 ;
int n , m , k ;
int f[maxn] , now[maxn] , suf[maxn] ;
int a[maxn] , b[maxn] , c[maxn] ;
struct node
{
	int x , y ;
	bool operator <(const node &s) const
	{
		if(y != s.y)  return y > s.y ;
		else  return y == s.y ;
	}
} ;
set<node> s ;
bool no()
{
    int sum = k ;
	for(int i = 1 ; i <= n ; i ++)
	{
		if(sum < a[i])  return 1 ;
		else  sum += b[i] ;
	}
	return 0 ;
}
void solve()
{
	int ans = 0 ;
	for(auto v : s)
	{
		int u = f[v.x] ;
		bool flag = 0 ;
		for(int i = u ; i <= n ; i ++)
		  if(now[i] - 1 < suf[i + 1]){flag = 1 ; break ;} 
		if(!flag)  
		{
		   for(int i = u ; i <= n ; i ++)	now[i] -- ;
		   ans += v.y ;
		}
	}
	printf("%d\n" , ans) ;
}
int main()
{
	scanf("%d%d%d" , &n , &m , &k) ;
	for(int i = 1 ; i <= n ; i ++)
	  scanf("%d%d%d" , &a[i] , &b[i] , &c[i]) ;
	for(int i = 1 ; i <= n ; i ++)  f[i] = i ;
	for(int i = 1 ; i <= m ; i ++)
	{
		int u , v ;
		scanf("%d%d" , &u , &v) ;
		f[v] = max(f[v] , u) ;
	}
	for(int i = n ; i >= 1 ; i --) 
	  suf[i] = max(a[i] , suf[i + 1] - b[i]) ;
	if(no()){printf("-1\n") ; return 0 ;}
	now[0] = k ;
	for(int i = 1 ; i <= n ; i ++)  now[i] = now[i - 1] + b[i] ;
	for(int i = 1 ; i <= n ; i ++)  s.insert(node{i , c[i]}) ;
	solve() ;
	return 0 ;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值