hdu1074 Doing Homework
传送门
题意
有(n(1leq nleq 15))项作业,每项作业都有名称、提交截止日期和完成这项作业所花费的日期,逾期一天扣一分,计算完成所有作业所扣的分数的最小值,并且输出按照时间顺序完成的作业名称
(n)项作业按照字典序从小到大输入,如果答案不唯一,输出字典序最小的解
题解
由于(n)不超过(15),可以通过状态压缩表示所有作业完成的状态,(1)表示已完成,(0)表示未完成
从小到大枚举所有状态,对于每一种状态,枚举所有未完成的作业,设完成这一作业的状态为新状态,更新新状态最少的扣分以及是从哪个状态转移过来的
由于作业是按照字典序从小到大排列的,更新的新状态是最后完成的作业,字典序最小的解是将字典序较大的作业放在后面,所以每次从后向前枚举所有作业
从状态((1<<n)-1)到状态(0)逆向找到每一次完成的作业,可以确定完成作业的顺序
时间复杂度(O(n*2^n))
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<sstream>
#include<cmath>
#include<ctime>
#include<climits>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=16;
int T,n,dp[1<<maxn],last[1<<maxn],sum[1<<maxn];
struct node{
int d,cost;
string name;
}a[maxn];
vector<string> ans;
int main(){
std::ios::sync_with_stdio(false);
cin>>T;
while(T--){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i].name>>a[i].d>>a[i].cost;
}
memset(sum,0,sizeof(sum));
int sta=1<<n;
for(int i=0;i<sta;i++){
for(int j=0;j<n;j++){
if(i&(1<<j)) sum[i]+=a[j].cost;
}
}
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=0;i<sta;i++){
for(int j=n-1;j>=0;j--){
if(i!=(i|(1<<j))){
if(dp[i|(1<<j)]>dp[i]+max(0,sum[i|(1<<j)]-a[j].d)){
dp[i|(1<<j)]=dp[i]+max(0,sum[i|(1<<j)]-a[j].d);
last[i|(1<<j)]=i;
}
}
}
}
cout<<dp[sta-1]<<"
";
int s=sta-1;
ans.clear();
while(s){
int t=last[s];
for(int i=0;i<n;i++){
if(((s>>i)&1)^((t>>i)&1)){
ans.push_back(a[i].name);
break;
}
}
s=t;
}
for(int i=n-1;i>=0;i--) cout<<ans[i]<<"
";
}
return 0;
}