BZOJ 1205: [HNOI2005]星际贸易

这道题要枚举做法QAQ

首先一看题目,乱七八糟一大堆费用利润什么的,不是DP就是最短路网络流。

网络流看起来好像会炸,而且模型很麻烦,弃疗。

发现Ai<10^9这个条件并无卵用,因为M<=2000,所以求最大贸易额是个背包问题。

DP之后可以建出一个DAG,然后可以找到最大贸易额的方案(然而方案很多岂不是要T

继续读题,发现方案唯一。

于是求出方案。

问题转化为在一个序列上经过好多点走到终点,符合各项条件的情况下费用最小,其中有某些点必须经过。

然后我就傻傻地写了个最短路QAQ还是N^4的。。。。。

好吧跳过枚举做法的过程。

最终的做法是DP。

设g[u][fu]表示将要离开点u而还没有离开(就是还没用那个反物质引擎)的时候有fu个燃料。

于是有g[u][fu]=min(g[u][fu-1]+p[u],g[v][fu+2])。

显然有两点需要判断的是当p[u]=0时前一个不能转移,后一个能转移必须要v不能超过u之前的最近一个必选点。

实际上r<10^9这个条件也是不必要的,因为最多只要2*n个燃料就够了,所以r的上界是2*n(md我这里写成2*n-2WA了4次)

所以就得到了一个N^3的做法(感觉好了一点)

事实上很容易发现把v和fu+2对调一下,就变成了min(g[fu+2][u-1],g[fu+2][u-2]......g[fu+2][v])。

这里他们的fu都是一样的,于是可以用单调队列维护fu相同时g[fu][v]的最小值。

所以就是N^2的了。

为毛我想了一个半小时还是只有RANK23QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf=1e9;
int f[2005][2005],g[2005][4005],a[2005],r,l0,l[2005],p[2005],c[2005];
bool ch[2005][2005],must[2005];
void print(int i,int j){
	if(!i)return;
	if(ch[i][j])must[i]=true,print(i-1,j-a[i]);
	else print(i-1,j);
}
int n,q[4005][2005],h[4005],t[4005];
int dp(){
	memset(g,0x3f,sizeof(g));
	memset(t,-1,sizeof(t));
	q[r][t[r]=0]=0;
	g[0][r]=0;
	for(int u=1;u<=n;u++)
	for(int fu=0;fu<=r;fu++){
		if(fu>0&&p[u])g[u][fu]=min(g[u][fu],g[u][fu-1]+p[u]);
		if(fu+2<=r){
			while(h[fu+2]<=t[fu+2]&&l[u]-l[q[fu+2][h[fu+2]]]>l0)h[fu+2]++;
			if(h[fu+2]<=t[fu+2])g[u][fu]=min(g[u][fu],g[q[fu+2][h[fu+2]]][fu+2]+c[u]);
		}
		while(h[fu]<=t[fu]&&g[u][fu]<g[q[fu][t[fu]]][fu])t[fu]--;
		if(must[u])t[fu]=h[fu]-1;
		q[fu][++t[fu]]=u;
	}
	int ans=inf;
	for(int i=0;i<=r;i++)ans=min(ans,g[n][i]);
	return ans;
}
int main(){
	//freopen("a.in","r",stdin);
	int m;scanf("%d%d%d%d",&n,&m,&r,&l0);
	r=min(r,2*n);
	for(int i=1;i<=n;i++){
		int b;
		scanf("%d%d%d%d%d",&a[i],&b,&l[i],&p[i],&c[i]);
		for(int j=m;j>=0;j--){
			f[i][j]=f[i-1][j];
			if(j>=a[i]&&f[i-1][j-a[i]]+b>f[i][j]){
				f[i][j]=f[i-1][j-a[i]]+b;
				ch[i][j]=1;
			}
		}
	}
	print(n,m);
	int ans=dp();
	if(ans>=inf)puts("Poor Coke!");
	else printf("%d %d\n",f[n][m],f[n][m]-ans);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值