hdu - 1864 - 最大报销额
题意:有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; }