HDU-1074 Doing Homework (状压dp+进制hash)

题目:
      传送门

思路:
      我们不妨先从贪心的角度出发,对于给定的 n个任务,我们肯定一个做完了以后立马再接着做另一个.所以可以得出来做完n个任务时的终止时间是固定的,即所有任务所需要的时间之和.
      那我们不妨状态压缩,状态s表示已经有哪些任务已经做了,那么很明显s状态下一定有个最小的扣分.所以dp[i]表示在状态i下的最少扣分,每次我们找到 不在状态i下的另一个任务,让其和状态 i 合并来更新下一个状态。
      然后我们现在要保证字典树最小,因为发现最多只有 15 个数,我们不妨用 15 进制将其hash成一个数字,然后我们只要比较数字就可以知道大小了.

AC_Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;

const int maxn = 16;
const int MAXN = (1<<15);
const int inf = 1e8;

struct P
{
    int cost,lit;
    char name[110];
}s[maxn];

int n;
char name[maxn][110];
long long dp[MAXN];
long long imp[MAXN];

int gettime(int x) {
    int sum =0;
    for(int i=0;x;i++) {
        if(x&1) sum += s[i].cost;
        x >>= 1;
    }
    return sum;
}

int main() {
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d",&n);
        for(int i=0;i<n;i++) {
            scanf("%s%d%d",s[i].name,&s[i].lit,&s[i].cost);
        }
        int up = (1<<n);
        for(int i=0;i<up;i++) {
            dp[i] = inf;
            imp[i]=0;
        }
        dp[0] =0;
        for(int i=0;i<up;i++) {
            int sums = gettime(i);
            // cout<<i<<' '<<dp[i]<<' '<<imp[i]<<endl;
            for(int j=0;j<n;j++) {
                int tmp = (1<<j);
                if((i&tmp)) continue;
                int news = (i|tmp);
                int val = (sums + s[j].cost <= s[j].lit ? 0:sums+s[j].cost-s[j].lit);
                if(val + dp[i] < dp[news]) {
                    dp[news] = dp[i]+val;
                    imp[news] = imp[i]*15+j;
                    // cout<<news<<endl;
                }
                else if(val + dp[i] == dp[news]) {
                    if(imp[i]*15+j < imp[news]) {
                        imp[news] = imp[i]*15+j;
                    }
                }
            }
        }
        stack<int> sta;
        printf("%d\n",dp[up-1]);
        for(int i=0;i<n;i++) {
            sta.push(imp[up-1]%15);
            imp[up-1] /=15;
        }
        while(!sta.empty()) {
            printf("%s\n",s[sta.top()].name);
            sta.pop();
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值