HDU6408 From ICPC to ACM

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6408

一上场读完这题,大概是个费用流防AK题吧,果然晚上dls讲题也说他第一眼费用流线段树加速找增广路(这什么东西哇,然后就跳过了,比完赛看了题解后,感觉以前noip最后3套模拟,cy的题有一道勇者斗恶龙也是这样,先不管用没用,先放进一个堆了,要用的时候再取最优的出来,算一蛤代价。这题有几个需要想到的结论,由于材料是无限的,那么c[i]更新为c[i-1]+R[i]表示从古至今最便宜的材料价格是多少,加上保存时间代价,那么这样我们当天生产的电脑的费用就是c[i]了,放进set中,我们还有一个保存电脑的费用,所以这个月卖ind月的电脑所要的费用是cost+pre[i-1]-pre[ind-1],表示从ind月生产出来的费用再加上每天保存的费用,总共费用是多少一台。那么每天晚上清仓的时候,我们把假设生产出来的电脑中费用最多的丢掉,只保留少于或等于e[i]台花费最小的假设生产出来的电脑。

attention:set里面装结构体,重载运算符要记住每个元素都要判不同,因为根据你的比大小要是相同了set就不会装进去了,set会把比大小相同的元素只装一个。

node x=(*s.begin())会把第一个元素的值赋值给x,x是个单独的结构体变量,x值的变化不会对s有影响

#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#define maxl 50010

using namespace std;

int k;
int c[maxl],d[maxl],m[maxl],p[maxl],e[maxl],R[maxl],E[maxl];
long long ans;
long long pre[maxl];
struct node
{
	int cost,res,ind;
	bool operator < (const node b) const
	{
		if(b.ind<ind)
		{
			if(cost!=pre[ind-1]-pre[b.ind-1]+b.cost)
				return cost<pre[ind-1]-pre[b.ind-1]+b.cost;
			else
				return ind<b.ind;
		}
		else
		{
			if(cost+pre[b.ind-1]-pre[ind-1]!=b.cost)
				return cost+pre[b.ind-1]-pre[ind-1]<b.cost;
			else
				return ind<b.ind;
		}	
	}
};
set <node> s;
set <node> ::iterator it;
bool flag;

inline int min(int a,int b)
{
	if(a<b)
		return a;
	else
		return b;
}

inline void prework()
{
	scanf("%d",&k);
	for(int i=1;i<=k;i++)
		scanf("%d%d%d%d",&c[i],&d[i],&m[i],&p[i]);
	for(int i=1;i<=k-1;i++)
		scanf("%d%d%d",&e[i],&R[i],&E[i]);
	int minic=c[1];
	for(int i=2;i<=k;i++)
		c[i]=min(c[i-1]+R[i-1],c[i]);
	for(int i=1;i<=k-1;i++)
		pre[i]=pre[i-1]+E[i];
	s.clear();
}

inline void prints()
{
	for(it=s.begin();it!=s.end();++it)
		printf("%d %d\n",it->cost,it->res);
}

inline void mainwork()
{
	int sum=0,num;flag=true;node x;ans=0;
	for(int i=1;i<=k;i++)
	{
		x.cost=c[i]+m[i];x.res=p[i];x.ind=i;
		s.insert(x);
		//prints();//打印set
		sum+=p[i];
		while(d[i])
		{
			if(sum==0 || s.begin()==s.end())
			{
				flag=false;
				return;
			}
			it=s.begin();
			x=(*it);
			//x.cost=it->cost;x.res=it->res;x.ind=it->ind; 
			s.erase(it);
			//prints();//打印set 
			if(x.res>d[i])
			{
				ans+=(x.cost+pre[i-1]-pre[x.ind-1])*d[i];
				x.res-=d[i];sum-=d[i];d[i]=0;	
				s.insert(x);
				//prints();//打印set
			}
			else
			{
				ans+=(x.cost+pre[i-1]-pre[x.ind-1])*x.res;
				d[i]-=x.res;sum-=x.res;x.res=0;
			}
		}
		num=sum-e[i];
		while(num>0)
		{
			it=s.end();it--;
			x=(*it);
			//x.cost=it->cost;x.res=it->res;x.ind=it->ind;
			s.erase(it);
			if(x.res>num)
				sum-=num,x.res-=num,num=0,s.insert(x);
			else
				sum-=x.res,num-=x.res;
		}
	}
}

inline void print()
{
	if(flag)
		printf("%lld\n",ans);
	else
		printf("-1\n");
}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值