D. Portals 思维+贪心+优先队列 /DP

2 篇文章 0 订阅
1 篇文章 0 订阅

D. Portals

Codeforces Round #608 (Div. 2)
题意《凑合看看》:
你要攻占敌人的城堡当且仅当全部攻占才有分数,你攻占城堡:
你可以选择在攻占的过程中放一个兵看守或选择在攻占之后某一个城堡后把派一个兵回来看守 或者干脆不要这个城堡的分数了,要你求最大的可得到的分数,给出每个城堡的需要兵力,获胜后兵数的增值,以及占领的分数。给出m个边表示城堡之间的连接。


–比赛一直想贪心但是没有想到怎么处理这些边,派人去占领城堡,怎么利用数据结构;; ,赛后,dalao代码果真简洁明了。


贪心想法:先跑一遍不设置兵看守城堡的,统计下一轮需要的兵力以及这一轮剩下的兵力 他们的差值就是这一轮目前看来最大空闲可以派去看守的兵数了。考虑这一轮的空闲兵数的一定是要比下一轮少的(因为这一轮的空闲兵数本身就是在攻占本轮的城堡之后,得到了新的兵力,考虑可以继续攻占下去剩下的兵数,但是下一轮的兵数在这一轮的基础上增加了新增兵力,)所以我们从后往前考虑。根据这个,我们在尽量后面的城堡,派兵占领前面的城堡应该是比较合理的[大体上就是说你是在第A轮派一个小兵攻占X堡垒的,那么他在第B轮(B>A),会有更多空闲的小兵能够执行看守任务]。所以我们考虑将每个城堡尽可能连接他比较后面的城堡。用一个优先队列来维护,使得堆里面的值永远是比较高的分数

~优先队列

#include<bits/stdc++.h>
using namespace std;
int a[5010],b[5010],c[5010];
int res[5010],r[5010],l[5010];vector<int>v[5010];
//v[]堡垒连接的点
//l[x] x连接的序号最大的堡垒
int main()
{
	priority_queue<int>q; 
	int n,m,k;cin>>n>>m>>k;int ok=1;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]>>b[i]>>c[i];
		res[i-1]=k-a[i];
		if(res[i-1]<0) ok=0;k+=b[i];//res这一轮的空闲兵数
	}
	for(int i=1;i<=n;i++) l[i]=i;
	for(int i=0;i<m;i++)
	{
		int x,y;cin>>x>>y;
		l[y]=max(l[y],x);
	}
	res[n]=5010;
	for(int i=n-1;i>=1;i--)
	{
		res[i]=min(res[i],res[i+1]);
	}
	for(int i=1;i<=n;i++)
	{
		v[l[i]].push_back(i);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<v[i].size();j++)
		{
			q.push(-c[v[i][j]]);//大根堆-取反->相当于小根堆
		}
		while(q.size()>res[i])
		{
			q.pop();
		}
	}
	if(ok==0)
	{
		printf("-1");return 0;
	}
	int s=0;
	while(!q.empty())
	{
		int t=q.top();
		s-=t;//因为上面的取反操作~
		q.pop();
	}
	printf("%d\n",s);
 } 

~DP写法

#include<bits/stdc++.h>
using namespace std;
const int N=5100;
int a[N],b[N],c[N],l[N],dp[N][N];
//dp[i][j]表示攻占到第i个堡垒,剩下j个兵力
vector<int>v[N]; 
int main()
{
	int n,m,k;
	cin>>n>>m>>k;int ok=1;int t=k;
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i]>>b[i]>>c[i];
    	if(t<a[i]) ok=0;
    	t+=b[i];
	}
	for(int i=1;i<=n;i++) l[i]=i;
	for(int i=1;i<=m;i++)
	{
		int x,y;cin>>x>>y;
		l[y]=max(x,l[y]);
	}
	for(int i=1;i<=n;i++)
	{
		v[l[i]].push_back(c[i]);//贪心 
	}
	if(!ok)
	{
		cout<<-1<<endl;
		return 0; 
	}
	memset(dp,-1,sizeof(dp));
	dp[1][k]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=5000;j++)
		{
			if(j<a[i]) continue;
			dp[i+1][j+b[i]]=max(dp[i+1][j+b[i]],dp[i][j]);//转移方程1 招兵 
		}
		for(int k=0;k<v[i].size();k++)
		for(int j=1;j<=5000;j++)
		{
			if(dp[i+1][j]==-1) continue;
			dp[i+1][j-1]=max(dp[i+1][j-1],dp[i+1][j]+v[i][k]);//转移方程2 派兵 
		}
	}
	int ans=0;
	for(int i=0;i<=5000;i++)
	{
		ans=max(ans,dp[n+1][i]);
	}
	cout<<ans<<endl;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值