HDU1074(状压DP)

题目链接

题意:有n门课,每门课都要交作业,作业有时间花费和最晚提交日期,如果超过日期交作业,每超过一天就要扣一分,问最少扣多少分并且给出最少扣分的方案。

题解:
因为最多只有15门课,所以可以压缩成一个数,二进制位上每一位代表一门课是否完成。然后开始DP。
枚举从1到1<< n的每一个状态i,然后枚举每一门课j(倒叙枚举,因为输入是按照字典序递增输入的),检查状态i是否完成了第j门课(可以通过与运算完成),如果完成了第j门课,计算不完成第j门课时所扣的分加上完成第j门课所扣的分是否比当前状态i所扣的分少,如果少于则进行更新分数和时间,并记下j作为路径输出。

#include
#include
#include
using namespace std;
const int maxn = (1 << 15) + 10;
const int inf = 99999999 ;
int dp[maxn],time[maxn],n,t,dead[20],cost[20],pre[maxn];
char s[20][105];
void solve()
{
    int maxm = 1 << n;
    for (int i = 1; i < maxm; i++)
    {
        dp[i] = inf;
        for (int j = n - 1; j >= 0; j--)
        {
            int temp = 1 << j;
            if(!(i & temp))continue;
            int score = time[i - temp] + cost[j] - dead[j];
            if(score < 0)score = 0;
            if(dp[i - temp] + score < dp[i])
            {
                dp[i] = dp[i - temp] + score;
                time[i] = time[i - temp] + cost[j];
                pre[i] = j;
            }
        }
    }
}
void print(int ans)
{
    if(!ans)return ;
    print(ans - (1<printf("%s\n",s[pre[ans]]);
}
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        memset(s,0,sizeof s);
        memset(dead,0,sizeof dead);
        memset(cost,0,sizeof cost);
        memset(time,0,sizeof dp);
        memset(pre,0,sizeof pre);

        scanf("%d",&n);
        for (int i = 0; i < n; i++)
            scanf("%s %d %d", &s[i],&dead[i],&cost[i]);

        solve();

        int ans = (1 << n) - 1;
        printf("%d\n",dp[ans]);
        print(ans);

    }
}

你可能感兴趣的:((~ ̄▽ ̄)~HDU,o(* ̄︶ ̄*)oDP)