题目链接:https://www.lanqiao.cn/problems/2195/learning/
费用报销
分析:
就是选和不选经典的01背包问题,具体描述看代码备注。
题目描述:
#include <iostream>
#include <algorithm>
using namespace std;
int n , M , k; //n张票据,M最大费用,日期差不小于k
const int N = 1010; //数据范围
int d[N] , me[N]; //d[]表示当前这天到1月1号的天数,me[]表示这天金额
int dp[N][5010] , ne[N][5010]; //dp[i][j]在前i个中选不超过j元的最大值,ne[][]当前选择的了前i个中的哪个
int sum[13] = {0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31}; //月份对应对的天数
struct Edge{
pair<int , int> a; //方便按日期排序
int me;
bool operator < (const Edge & A)const{ //重写小于
return a < A.a;
}
}Edge[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> M >> k;
for(int i = 1;i <= n;i++) //输入n个数据
{
int m , t , e;
cin >> m >> t >> e;
Edge[i].a = {m , t};
Edge[i].me = e;
}
sort(Edge + 1, Edge + n + 1); //日期从小到大排序
for(int i = 1;i <= n;i++) //计算到1月1号的天数(包含1月1号这天)
{
for(int j = 1;j < Edge[i].a.first;j++)
{
d[i] += sum[j];
}
d[i] += Edge[i].a.second;
}
for(int i = 1;i <= n;i++)
{
for(int j = 0;j <= M;j++)
{
if(i > 1) //除第一个数据记录先不选自己的值
{
dp[i][j] = dp[i - 1][j];
ne[i][j] = ne[i - 1][j];
}
if(j >= Edge[i].me)
{
if(dp[i][j] < dp[i - 1][j - Edge[i].me] + Edge[i].me) //如果产生最大值
{
if(ne[i - 1][j - Edge[i].me] == 0 || d[i] - d[ne[i - 1][j - Edge[i].me]] >= k)
//且要么前面没选要么日期差大于k
{
dp[i][j] = dp[i - 1][j - Edge[i].me] + Edge[i].me;
ne[i][j] = i;
}
else //如果产生最大值但是不能选则保持原样
{
dp[i][j] = dp[i][j - 1];
ne[i][j] = ne[i][j - 1];
}
}
}
}
}
cout << dp[n][M]; //输出最后的答案
return 0;
}