啊这是一道省选题,怪不得我不会写,最重要的是战胜自己的内心要坚信你会写出来,恩。
用f[i][j]来表示前i个月还有j吨货的费用,就是依靠 上一月的费用+上一月的货的存费+订需要的+订(j-k)的费用
f[i][j]=min(f[i-1][k]+k*m+d[i]*(u[i]+(j-k))); ( 0<j,k<s) -> f[i][j]=min(f[i-1][k]-d[i]*k+k*m)+d[i]*(u[i]+j);
由于k的范围是从0到s 而s大的不止一点,所以一步步循环就会超时,因此需要用队列来优化,跟 超级教主 相似,都是单调递减,队首为最优 之后一步步把队尾踢出去换最优。
由于超级教主都是固定的值,而这玩意你只能确定每一回的值,因此需要做N遍超级教主!
先确定f[i][0] 因此就要补充,-> 上一月的存货k<这个月的需要 来算出上一月的+补充=需要的 最优。
之后为了下一月的f[i][0] 就要提前算出这个月的f[i][j]各种情况 因此再有 上一月的存货k<s 队首最优,算出f[i][j]
答案就为f[n][0]。
#include<iostream> #include<algorithm> #include<cstring> using namespace std; int n,m,s,u[55],d[55]; int f[52][10010],que[100010]; int head=0,tail=0; void work(int i,int k) { while(head<tail&&(f[i-1][k]+k*(m-d[i])<f[i-1][que[tail-1]]+que[tail-1]*(m-d[i]))) tail--; que[tail++]=k; } int main() { cin>>n>>m>>s; for(int i=1;i<=n;i++) cin>>u[i]; for(int i=1;i<=n;i++) cin>>d[i]; memset(f,10,sizeof(f)); f[0][0]=0; for(int i=1;i<=n;i++) { head=tail=0; for(int k=0;k<=u[i];k++) work(i,k); int t=que[head]; f[i][0]=f[i-1][t]+t*(m-d[i])+d[i]*u[i]; for(int j=1;j<=s;j++) { if(j+u[i]<=s) work(i,j+u[i]); int k=que[head]; f[i][j]=f[i-1][k]+k*m+d[i]*(u[i]+j-k); } } cout<<f[n][0]; return 0; }
调试时莫名出现输入输出,一个bug吧,这道题队列应该是最优解法,然而姬树流大大早已想出简单DP,然而看不懂,还有一个做法是费用流,费用流是什么鬼。以后补上。