课程大作业

终于考完了两门通选课,可以开始欢快的程设训练了。

课程大作业 POJ15288

刚开始想先用一个dp[T][n][n]记录在时间t刚好完成第i项作业的最短完成序列,这样的复杂度是n2的。然而这么做每一个子状态是有后效性的,所以挂了。于是看了一下大佬的代码。观察到这里每一时间选择什么课的罚分只由选课本身决定,所需要记录的只有前面的课程有哪些,而不是课程的顺序,这样就将一个n!的复杂度化为2n。但是怎么实现仍然是一个巨大的问题,要用到位运算优化代码,状压dp。

另外,还学到了原来string可以这么输出回车~这样的话答案也可以状压了,大佬就是大佬!

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <string.h>
using namespace std;
const int MAX=1<<16;
int dp[MAX];//表示把每一个状态中课程全部完成的最少用时
int t[20];//表示每一门课程的完成时间
int d[20];//表示每一门课程的ddl
int sum[MAX];//表示某一状态的完成时间总和
string name[20];
string ans[MAX];
int calc(int a,int b){
    return max(0,a-b);
}
int main()
{
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        for(int i=0;i<n;++i){
            cin>>name[i]>>d[i]>>t[i];sum[1<<i]=t[i];
        }
        for(int i=1;i<(1<<n);++i){
            for(int j=0;j<n;++j){
                if(i&(1<<j)){
                    sum[i]=sum[i^(1<<j)]+t[j];
                }
            }
        }
        dp[0]=0;
        for(int i=0;i<(1<<n);++i)ans[i]="";//初始化
        for(int i=1;i<(1<<n);++i){
            for(int j=0;j<n;++j){
                if(i&(1<<j)&&(dp[i^(1<<j)]+calc(sum[i],d[j])<=dp[i]||ans[i]=="")){
                    if(dp[i^(1<<j)]+calc(sum[i],d[j])<dp[i]||ans[i^(1<<j)]+name[j]+"\n"<ans[i]||ans[i]==""){
                        ans[i]=ans[i^(1<<j)]+name[j]+"\n";
                    }
                    dp[i]=dp[i^(1<<j)]+calc(sum[i],d[j]);
                }
            }
        }
        cout<<dp[(1<<n)-1]<<endl;
        cout<<ans[(1<<n)-1];
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yhjpku/article/details/80698249