Doing Homework
https://vjudge.net/problem/HDU-1074/origin
前言
第一次学状压 dp
,它的思想和 dp
的思想一致,根据前一个状态来推取后一个状态。
就像这样 (dp[11_2]=min(dp[10_2],dp[01_2]))
使用二进制来进行表示状态。
(000_2) 表示一个物品也没有进行选择
(001_2 = 0*2^2+0*2^1+1*2^0) 表示选择了第 (1) 个物品
(011_2 = 0*2^2+1*2^1+1*2^0) 表示选择了第 (1、2) 个物品
(101_2 = 1*2^2+0*2^1+1*2^0) 表示选择了第 (1、3) 个物品
进而理解,选择第 (x) 个物品的时候,需要对前面没有选过 (x) 这个物品的状态进行求解最小值,而 (((1<<x)&pos)==0) 表示 (pos) 这个状态没有进行选择物品 (x),
(((1<<x)|pos)==0) 表示在 (pos) 这个状态的基础上进行选择物品 (x),
进而对添加该物品的状态进行求最小值。
即 (dp[(1<<x)|pos]=min(dp[pos]+val[x]))
感觉应该可以理解,如果有不理解,希望大佬们在评论区询问。
(Orz)
题意:
(T) 组数据,每个数据有 (n) 个课程。
对于 (n) 个课程,包含三个属性课程名,作业的截止日期和完成作业需要的天数。
作业完成情况拖延一天结果就是扣一定分数。
题解
通过状压 (dp) 进行求解,
在枚举状态的时候需要保存它的前一个状态。
之后使用 (dfs) 进行求出所有解。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
struct node{
char s[105];
int l,r;
}num[20];
int dp[1<<16];
int pre[1<<16];
bool cmp(node x,node y){
int lx=strlen(x.s);
int ly=strlen(y.s);
for(int i=0;i<lx&&i<ly;++i){
if(x.s[i]==y.s[i]);
else return x.s[i]<y.s[i];
}
return lx<ly;
}
void dfs(int pos,int n){
if(!pos)return;
int x;
for(int i=0;i<n;++i){
if(((1<<i)&pos)&&!(pre[pos]&(1<<i))){
x=i;
}
}
dfs(pre[pos],n);
printf("%s
",num[x].s);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
memset(dp,INF,sizeof(dp));
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%s%d%d",num[i].s,&num[i].l,&num[i].r);
}
// sort(num,num+n,cmp);
// for(int i=0;i<n;++i)
// printf("%s
",num[i].s);
dp[0]=0;
for(int i=0;i<(1<<n);++i){
for(int j=0;j<n;++j){
if((1<<j)&i)continue;
int val=num[j].r;
for(int k=0;k<n;++k){
if((1<<k)&i)
val+=num[k].r;
}
val=val>num[j].l?val-num[j].l:0;
if(dp[(1<<j)|i]>dp[i]+val){
dp[(1<<j)|i]=dp[i]+val;
pre[(1<<j)|i]=i;
}
}
}
printf("%d
",dp[(1<<n)-1]);
dfs((1<<n)-1,n);
}
return 0;
}
后记
这个题好像有点问题诶,题目最后要求的是按字典序进行输出,因为怕 (WA) 预处理了一下所有物品 (A) 了,但是把预处理关了也过了。
仿佛所有的解只有一个结果不需要进行处理的诶。