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;
}



posted @ 2013-01-31 19:00  xiaodanding  阅读(102)  评论(0编辑  收藏  举报