Doing Homework HDU - 1074
题意:
有n个任务,每个任务有一个截止时间,超过截止时间一天,要扣一个分。
求如何安排任务,使得扣的分数最少。
(1<=n<=15)
题解:
n很小,可以往状压dp方向去想
我们用i枚举所有的状态,然后对于第j个任务,tmp=i-(1<<j),tmp表示状态i在未完成j的前一个状态
tim[]表示当前状态所花费的时间,那么完成第j个任务所扣分数为score=tim[tmp]+cost[j]-dead[j],完成tmp所需时间+完成任务j所需时间-截止日期,超出多少扣多少分数
dp[i]表示i状态下的的最小扣分,dp[i]=min(dp[tmp]+score)
为了方便答案的输出,我们用pre[i]记录,最后一个完成状态i所办的任务,pre[i]=j,这样记录是为了之后方便输出,w=i-(1<<pre[i])就是在任务j之前的状态,然后依次寻找w-(1<<pre[w])
在循环任务j时,要从后向前找,因为题目所给的数据是字典序递增的,而我们最后的输出是根据pre倒着输出的,所以逆着寻找可以保证输出的字典序最小
代码:
我的代码不知道哪里错了,一直wa,先贴上网上的正代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 15
#define MOD 10007
#define E 1e-6
#define LL long long
using namespace std;
char str[20][101];
int dp[1<<N],pre[1<<N],times[1<<N];
int dead[N],cost[N];
void print(int x){//递归输出作业顺序if(!x)return;print(x-(1<<pre[x]));printf("%s\n",str[pre[x]]);
}
int main()
{int t;scanf("%d",&t);while(t--){int n;scanf("%d",&n);for(int i=0;i<n;i++)scanf("%s%d%d",&str[i],&dead[i],&cost[i]);memset(times,0,sizeof(times));memset(pre,0,sizeof(pre));for(int i=1;i<(1<<n);i++){//枚举所有状态dp[i]=INF;//初始化状态i的扣分for(int j=n-1;j>=0;j--){//从后向前找int temp1=1<<j;if(!(i&temp1))//状态i不存在作业j完成的情况continue;int temp2=i-temp1;//未完成作业j的状态int score=times[temp2]+cost[j]-dead[j];//计算扣分if(score<0)//最小扣分为0score=0;if(dp[i]>dp[temp2]+score){dp[i]=dp[temp2]+score;//记录最小扣分times[i]=times[temp2]+cost[j];//到达状态i所花费的时间pre[i]=j;//状态i的前驱}}}printf("%d\n",dp[(1<<n)-1]);print((1<<n)-1);}return 0;;
}