首先一看题目,乱七八糟一大堆费用利润什么的,不是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; }