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\n",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\n",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\n",dp[(1<<n)-1]);
dfs((1<<n)-1,n);
}
return 0;
}
这个题好像有点问题诶,题目最后要求的是按字典序进行输出,因为怕 \(WA\) 预处理了一下所有物品 \(A\) 了,但是把预处理关了也过了。
仿佛所有的解只有一个结果不需要进行处理的诶。
原文:https://www.cnblogs.com/VagrantAC/p/12724177.html