费用报销(01背包变种问题)

费用报销

首先要处理好月日转为净天数
给出月和日转化为净天数教程

给出n个物品求最大价值,这明显是01背包,并且他的体积变成了价值有限制。但是又因为有一个相邻日期大于等于K的限制,所以并不是每个物品可以相邻,也就是01背包的动态转移方程为f(i,j)=max(f(i-1,j),f(i-1,j-w)+v) ,当拿走第i个物品时一定考虑他和上一个物品最近日期超过K这一合法性判断,所以我们要预处理每个物品最近日期并且大于等于K天的相邻物品存放到一个数组里,这样每次拿走第i个物品时,只需要从他距离日期最近并且合法的物品转移过来即可。

动态转移方程:f[i][j]=max(f[i-1][j], f[上一次临界合法的发票][j-wi]+vi);

#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N=1e4+10;
int n,m,k;
int yue[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; 
int s[20];//存放每月初的净天数 
int f[N][N],lst[N]; 

struct ren{
	int m,d,v,t;//月、天、价值、净天数 
}d[N]; 

bool cmp(ren a,ren b){
	return a.t<b.t; 
} 

int main(){
     
     
	 cin>>n>>m>>k; 
	 for(int i=2;i<=12;i++)s[i]=s[i-1]+yue[i-1]; //前缀和处理净天数
	  
     for(int i=1;i<=n;i++){
     	 cin>>d[i].m>>d[i].d>>d[i].v;
		  d[i].t=s[d[i].m]+d[i].d; 
	 } 
	 sort(d+1,d+1+n,cmp);
	 
	 for(int i=1;i<=n;i++){
	 	for(int j=1;j<i;j++){
	 		if(d[i].t-d[j].t>=k)lst[i]=j; //找出距离i最近合法的票据 
		 } 
	 } 
	 //背包dp 
	 for(int i=1;i<=n;i++){
	 	for(int j=m;j>=d[i].v;j--){
	 		f[i][j]=max(f[i-1][j],f[lst[i]][j-d[i].v]+d[i].v); 
		 } 
	 } 
	 
	 cout<<f[n][m]<<endl; 
	  
    
     
     


	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值