算法核心:状态压缩DP
大意:
有n门课程作业,每门作业的截止时间为D,需要花费的时间为C,若作业不能按时完成,每超期1天扣1分。
这n门作业按课程的字典序先后输入
问完成这n门作业至少要扣多少分,并输出扣分最少的做作业顺序
PS:达到扣分最少的方案有多种,请输出字典序最小的那一组方案
分析:
n<=15,由题意知,只需对这n份作业进行全排列,选出扣分最少的即可。
用一个二进制数存储这n份作业的完成情况,第1.。。。n个作业状况分别
对应二进制数的第0,1.。。。。,n-1位则由题意,故数字上限为2^n
其中 2^n-1即为n项作业全部完成,0为没有作业完成。。。
用dp[i]记录完成作业状态为i时的信息(所需时间,前一个状态,最少损失的分数)。
递推条件如下
1.状态a能做第i号作业的条件是a中作业i尚未完成,即a&i=0。
2.若有两个状态dp[a],dp[b]都能到达dp[i],那么选择能使到达i扣分小的那一条路径,若分数相同,转入3
3.这两种状态扣的分数相同,那么选择字典序小的,由于作业按字典序输入,故即dp[i].pre = min(a,b);
初始化:dp[0].cost = 0;dp[0].pre=-1;dp[0].reduced = 0;
大意:
有n门课程作业,每门作业的截止时间为D,需要花费的时间为C,若作业不能按时完成,每超期1天扣1分。
这n门作业按课程的字典序先后输入
问完成这n门作业至少要扣多少分,并输出扣分最少的做作业顺序
PS:达到扣分最少的方案有多种,请输出字典序最小的那一组方案
分析:
n<=15,由题意知,只需对这n份作业进行全排列,选出扣分最少的即可。
用一个二进制数存储这n份作业的完成情况,第1.。。。n个作业状况分别
对应二进制数的第0,1.。。。。,n-1位则由题意,故数字上限为2^n
其中 2^n-1即为n项作业全部完成,0为没有作业完成。。。
用dp[i]记录完成作业状态为i时的信息(所需时间,前一个状态,最少损失的分数)。
递推条件如下
1.状态a能做第i号作业的条件是a中作业i尚未完成,即a&i=0。
2.若有两个状态dp[a],dp[b]都能到达dp[i],那么选择能使到达i扣分小的那一条路径,若分数相同,转入3
3.这两种状态扣的分数相同,那么选择字典序小的,由于作业按字典序输入,故即dp[i].pre = min(a,b);
初始化:dp[0].cost = 0;dp[0].pre=-1;dp[0].reduced = 0;
最后dp[2^n-1].reduced即为最少扣分,课程安排可递归的输出
(bin神的分析写的很赞= =!搬到此处,原文地址:http://www.cnblogs.com/kuangbin/archive/2011/08/04/2127687.html)
感想:
1、递归输出的时候将状态转化为对应课程下标时要注意,由于是下标为0的表示第一门课程,所以要减去1。
也可以
void out(int state)
{
int x=DP[state].pre^state;
int cnt=0;
x>>=1;
while(x)
{
cnt++;
x>>=1;
}
if(DP[state].pre!=0)
out(DP[state].pre);
printf("%s\n",course[cnt].name);
}
2、二进制的运算一定要加括号啊啊啊啊!!!
#include<cstdio>
#include<iostream>
#include<cstring>
#define MAX 1<<16
using namespace std;
struct Course
{
int cost;
int deadline;
int name[105];
}course[16];
struct node
{
int pre;
int reduced;
int cost;
}DP[MAX];
bool vis[MAX];
void out(int state)
{
int x=DP[state].pre^state;
int cnt=0;
while(x)
{
x>>=1;
cnt++;
}
if(DP[state].pre!=0)
out(DP[state].pre);
printf("%s\n",course[cnt-1].name);
}
int main()
{
int T,i,j,n,cc,reduce,curtmp,cur;
scanf("%d",&T);
while(T--)
{
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%s%d%d",&course[i].name,&course[i].deadline,&course[i].cost);
DP[0].pre=-1;
DP[0].cost=0;
DP[0].reduced=0;
vis[0]=true;
int zz=(1<<n)-1; //位运算加括号!!!
for(i=0;i<zz;i++)
{
for(j=0;j<n;j++)
{
cur=1<<j;
if((i&cur)==0)
{
curtmp=i|cur;
cc=DP[i].cost+course[j].cost;
DP[curtmp].cost=cc;
reduce=cc-course[j].deadline;
if(reduce<0) reduce=0;
reduce+=DP[i].reduced;
if(vis[curtmp])
{
if(reduce<DP[curtmp].reduced)
{
DP[curtmp].reduced=reduce;
DP[curtmp].pre=i;
}
}
else
{
vis[curtmp]=true;
DP[curtmp].pre=i;
DP[curtmp].reduced=reduce;
}
}
}
}
printf("%d\n",DP[zz].reduced);
out(zz);
}
return 0;
}