题意:给出 n 种作业,然后给出每种作业交作业的截至日期,和完成这项任务需要的时间。如果在截至日期后交作业,那么每晚一天就要扣一分。求出扣分最少为多少,并且打印最小扣分完成作业的方案,n <=15。
看一下 n,很小只有15,最开始想的就是暴力打印完成这 15 项作业的所有方案,那么就是 15! ,就明显T了。
那么考虑一下DP,开一个数组,dp[i] ,表示完成 i 种课程的最小扣分,但是还是想不到怎么转移,对于 i 种课程有很多种可能,那么就再加一维,dp[i][j],表示的是完成 i 种课程,并且这 i 种课程分别是 j的二进制。那么就按照状压DP的思路写起走就行了。分别枚举 i ,j,然后再来转移。
然后再来考虑一下打印路径的问题,对于每一个dp[i][j],纪录转移到这个状态的前一个 j ,和新加入的课程,也就是两个前驱,再打印就可以了。
题目要求是要按照字典序来打印,这里没有管字典序的问题也能过,想不通。
#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
#include<math.h>
#include<map>
#include<iostream>
using namespace std;
#define maxn 100005
struct node
{
int use;
int time;
int preclass;
int prej;
}dp[16][1<<16];
struct nnode
{
char name[110];
int dead;
int day;
}a[20];
int n,mask;
vector<int> s[20];
char ans[20][110];
int solve(int x)
{
int cnt=0;
int dx=x;
while(dx)
{
if(dx%2==1)
cnt++;
dx/=2;
}
return cnt;
}
int main()
{
int t;
for(int i=0;i<(1<<15);i++)
{
int cnt=solve(i);
s[cnt].push_back(i);
}
scanf("%d",&t);
while(t--)
{
memset(dp,0x7f,sizeof(dp));
scanf("%d",&n);
mask=(1<<n)-1;
for(int i=0;i<n;i++)
scanf("%s%d%d",a[i].name,&a[i].dead,&a[i].day);
for(int i=0;i<=mask;i++)
dp[0][i].time=dp[0][i].use=0;
for(int i=1;i<=n;i++)
{
int len=s[i-1].size();
for(int jj=0;jj<len;jj++)
{
int j=s[i-1][jj];
for(int k=0;k<n;k++)
{
int tmp=(1<<k);
if((j&tmp)==0)
{
int now=(j|tmp);
int tmptime=dp[i-1][j].time+a[k].day-a[k].dead;
if(tmptime<=0)
tmptime=0;
tmptime+=dp[i-1][j].use;
if(dp[i][now].use>tmptime)
{
dp[i][now].use=tmptime;
dp[i][now].time=dp[i-1][j].time+a[k].day;
dp[i][now].prej=j;
dp[i][now].preclass=k;
}
}
}
}
}
printf("%d\n",dp[n][mask].use);
int now=mask;
int cnt=0;
for(int i=n;i>=1;i--)
{
strcpy(ans[i],a[dp[i][now].preclass].name);
now=dp[i][now].prej;
}
for(int i=1;i<=n;i++)
printf("%s\n",ans[i]);
}
return 0;
}
本文介绍了一种使用状态压缩动态规划(状压DP)的方法来解决一项关于作业安排的问题,该问题需要找到完成所有作业的最优方案,使得因延迟提交而被扣除的分数最少。文中提供了一个详细的代码实现过程。
655

被折叠的 条评论
为什么被折叠?



