题目大意:
有N门科目作业,每个作业都有规定限制完成的时间和做完所需的天数,如果每超一天,就会扣1分,问。如何能使扣的分数最少,且输出做作业的顺序(如果有多个相同的扣减分数,输出字典序最小的那个)
做法:状态压缩DP,贪心只能算出那个值,并不能照字典序输出。
看了某大牛的题解才会的,还是太渣Orz.
如:
A 5 5
B 6 5
C 5 5
贪心做的话,照deadline排序,然后选择,会输出
14
A
C
B
但是答案应该是
14
A
B
C
作业最多是15.二进制表示所有状态有2^15-1种,不大。
假如n=4;1111(2)表示全部作业都完成了的状态。
1110表示第一份作业没完成
dp[i](0 <= i < 2^n)里面包括用了几天,前一个状态,扣的分数。
0 <= j < n.
(i&1<
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define INF 0x7fffffff
#define ll long long
#define REP(i,n) for(int i=0;i<(n);i++)
#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)
#define sd(n) scanf("%d",&(n))
#define pd(n) printf("%d\n",(n))
#define maxn 1<<16
int flag[maxn];
struct node
{
int pre;
int day;
int reduced;
}dp[maxn];
struct work
{
int deadline;
int cost;
char sj[105];
}w[16];
void output(int n)
{
if(n==0)
return;
else
{
int now=n;
int cp=dp[n].pre;
output(cp);
int count=0;
while((now%2)==(cp%2)){
count++;
now/=2;
cp/=2;
}
printf("%s\n",w[count].sj);
}
}
int main()
{
int t;
sd(t);
while(t--){
int n;
sd(n);
int kong=(1<<n)-1;
REP(i,n)
{
scanf("%s%d%d",w[i].sj,&w[i].deadline,&w[i].cost);
}
dp[0].pre=-1;dp[0].day=0;dp[0].reduced=0;
memset(flag,0,sizeof flag);
for(int i=0;i<kong;i++)
{
for(int j=0;j<n;j++)
{
int zk=1<<j;
if((i&zk)==0)
{
int now=i|zk;
dp[now].day=dp[i].day+w[j].cost;
int reduce=dp[now].day-w[j].deadline;
if(reduce<0)reduce=0;
reduce+=dp[i].reduced;
if(flag[now])
{
if(reduce<dp[now].reduced)
{
dp[now].reduced=reduce;
dp[now].pre=i;
}
}
else
{
dp[now].reduced=reduce;
dp[now].pre=i;
flag[now]=1;
}
}
}
}
printf("%d\n",dp[kong].reduced);
output(kong);
}
}