标签:dp
这道题想了很久,总觉得状态太多,情况太多,用dp根本不知道该怎么写
看了网上的解题报告,说是用状压dp,即dp[i]记录完成哪几个作业所扣分的最小值,然后递推公式是:dp[i]=min(dp[i],max(dp[i-(1<<j)]+max(sum[i]-dead[j],0));
sum[i]即到该状态时已经花费的时间,cost[j]表示第j个作业需要花费的赶时间,dead[j]表示第j个作业的deadline
由于考虑第i个状态的时候,i状态可以由好几个状态推过来,即该状态的最后一个数加进来的种种顺序都已经考虑到了,这样就保证了公式的准确性。
然后说一说这道题和前面几道题的区别,在这道题中,由前i-1道题的规模推至前i道题时,由于第i道题目放置的位置可能有多种,而每一种又都会影响到前面的结果,这样就导致这样的递推方式是行不通的,而前一道mokey banana的题中,因为下一个方块的加入只和顶层的方块有关系,所以我们加一个标识位表示最顶层方块是什么,这就使得下一个方块的加入不会影响前面方块的一个最值。而这道题中,我们用哪几数组合的最小值来使得后一状态的加入不会影响到前一状态,很巧妙的一个思想。
貌似写得有点乱,dp就是这样,很多东西真的是只能意会,不能言传,还是要自己多想想
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int inf=0x7fffffff; const int N=1<<15+10; int dp[1<<15],sum[1<<15],order[1<<15],dead[20],cost[20]; char s[20][105]; void init(){ dp[0]=0; memset(sum,0,sizeof(sum)); memset(order,0,sizeof(order)); } void output(int maxn){ if(maxn==0) return; int tmp=maxn&(1<<order[maxn]); output(maxn-tmp); printf("%s\n",s[order[maxn]]); } int main(){ #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int t,n; scanf("%d",&t); while(t--){ init(); scanf("%d",&n); int maxn=1<<n; for(int i=0;i<n;i++) scanf("%s%d%d",s[i],&dead[i],&cost[i]); for(int i=1;i<maxn;i++){ dp[i]=inf; for(int j=0;j<n;j++){//从前往后以保证输出时字典序最小 int k=i&(1<<j); if(k){ sum[i]=sum[i-(1<<j)]+cost[j]; int tmp=dp[i-(1<<j)]+max(sum[i]-dead[j],0); if(tmp<=dp[i]){ dp[i]=tmp; order[i]=j; } } } } maxn--; printf("%d\n",dp[maxn]); //for(int i=1;i<=maxn;i++) printf("%d\n",dp[i]); output(maxn); } return 0; }
标签:dp
原文地址:http://blog.csdn.net/lj94093/article/details/45269595