题意:有N张账单,但只是账单上只有A、B、C类物品的,单类物品账目不超过600的,单张账单账目总和不超过1000的才可报销,问在不超过Q的前提下最多可报销多少元。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1864
——>>题目中说“单项物品的价值不得超过600元”,我总以为是输入时的单个项的账目不超过600,却不想要把账单里的同一类的加起来再与600比较,WA了N次……背包问题,注意剪枝即可。(可以0MS的。。。)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <iomanip>
#include <algorithm>
using namespace std;
const int maxn = 30000 + 10;
double d[maxn], MAX, able[31], Q; //MAX为最终要输出的结果,Q为允许报销额
int cnt;
bool cmp(const double &e1, const double &e2) //让账目从大到小排序
{
return e1 > e2;
}
void dp(int cur, double cur_sum) //第cur张可报销的账单,目前已报销了cur_sum元
{
if(abs(cur_sum - Q) < 1e-14) //如果已达最大允许,则只能这么大了,返回
{
MAX = Q;
return;
}
if(cur == cnt) //当所有的可报销账单检查完时
{
MAX = max(MAX, cur_sum); //更新MAX
return;
}
double sum = cur_sum; //范了两次错误了!!!
for(int i = cur; i < cnt; i++) //剪枝,如果将剩下的全部加起来都不如现在的大,返回
sum += able[i];
if(sum <= MAX) return;
if(cur_sum+able[cur] <= Q) dp(cur+1, cur_sum+able[cur]); //报销这张
dp(cur+1, cur_sum); //不报销这张
}
int main()
{
double cost; //输入用中间变量
int N, i, j, m; //N张发票
char s; //输入用中间变量
bool vis[31];
while(cin>>Q>>N)
{
if(!N) return 0;
memset(vis, 0, sizeof(vis)); //预置vis为0,即所有账单均可报销
cnt = 0; //初始化可报销的账单总数为0
for(i = 0; i < N; i++) //N张发票
{
cin>>m;
double sum, A_sum = 0.0, B_sum = 0.0, C_sum = 0.0; //sum为所有类账目总和,A_sum、B_sum、C_sum分别为A、B、C类账目总和
for(j = 0; j < m; j++) //各张发票上的各个项目
{
cin.get(); //吸收标记
scanf("%c:%lf", &s, &cost);
if(s != 'A' && s != 'B' && s != 'C') //如果不是A、B、C,不可报销
{
vis[i] = 1;
break;
}
if(s == 'A') A_sum += cost; //计算各类账目总和
else if(s == 'B') B_sum += cost;
else C_sum += cost;
}
sum = A_sum + B_sum + C_sum; //该张账单账目总和
if(A_sum > 600.0 || B_sum > 600.0 || C_sum > 600.0 || sum > 1000.0) vis[i] = 1; //单类超过600,总和超过1000,不可报销
if(!vis[i]) able[cnt++] = sum; //记录可报销的账单
}
sort(able, able+cnt, cmp); //从大到小排序剪枝
MAX = 0; //可获最大报销额
dp(0, 0); //dp求解
cout<<setiosflags(ios::fixed)<<setprecision(2)<<MAX<<endl;
}
return 0;
}
今天再做一次,代码少了,只是时间和空间的开销大了……(滚动数组)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int dp[3000000+10];
int main()
{
int N, n, a[40], i, j;
char c;
double Q, d;
bool vis;
while(~scanf("%lf%d", &Q, &N))
{
if(!N) return 0;
int m = 0;
while(N--)
{
scanf("%d", &n);
double a_sum = 0, b_sum = 0, c_sum = 0;
vis = 1;
while(n--)
{
scanf(" %c:%lf", &c, &d);
if(c != 'A' && c != 'B' && c != 'C')
{
vis = 0;
continue;
}
if(c == 'A') a_sum += d;
if(c == 'B') b_sum += d;
if(c == 'C') c_sum += d;
}
double sum = a_sum+b_sum+c_sum;
if(a_sum > 600 || b_sum > 600 || c_sum > 600 || sum > 1000) vis = 0;
if(vis) a[++m] = (int)(sum*100);
}
int q = (int)(Q*100);
memset(dp, 0, sizeof(dp));
for(i = 1; i <= m; i++)
for(j = q; j >= 0; j--)
if(j-a[i] >= 0) dp[j] = max(dp[j], dp[j-a[i]]+a[i]);
printf("%.2lf\n", (double)dp[q]/100.0);
}
return 0;
}