hdu 1074 Doing Homework(记忆化搜索+状态压缩)

这道题被我拖很久了。

先是大概4月份的时候吧,看了一下。放弃了。

3,4天前有看了一下,当时去写了个n!的暴力,妥妥的T了。

然后注意到n的范围很小,肯定是压缩一下状态。

我艹,压缩什么?0和1不是表示有没有选吗?

想了几分钟,又放弃了。

然后碰巧最近写了几个记忆化搜索,我发现如果你递推不扎实的话

先练好搜索,然后写记忆化也一般没有什么大事。

然后也有看状压的pdf,但是似乎都是在一个棋盘上搞来搞去(我艹,无奈)。

今天早上起来时,忽然对这个题有些想法,当时大概的思路是

这样的。从0开始搜到整个序列都是1,还以为自己的程序是记忆化。

其实呢,很容易证明这个想法是错的,从不同顺序搜到整个序列都是1的答案

应该是一样的(好像没有表达清楚)。

那么正解是怎么搜呢?

应该从整个序列都是1搜到0。。。为什么?画个多叉树的图,感受一下。这样记忆化才有意义。

但是。。。。。。。。。

时间怎么搞呢?就是我们的边界怎么处理呢?很明显肯定是第一个就是边界。

但是我们保存哪个时间呢?

我又陷入无奈。。。。。

其实可以这样,你先把所有情况的时间都预处理出来(具体见代码)。

这样写,应该是最简单的。。。网上各种代码抄来抄去我也奉献一个新鲜的血液吧。。。

/*
    author:ray007great
    version:1.0
*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<set>
#include<string>
#include<queue>
using namespace std;
typedef long long ll;
/*  define */

#define sf(a) scanf("%d",&a)
#define sfs(a) scanf("%s",a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repd(i,a,b) for(int i=(a);i>=(b);i--)
#define rep1(i,a,b) for(int i=(a);i<(b);i++)
#define clr(a) memset(a,0,sizeof(a))
#define clr1(a) memset(a,-1,sizeof(a))
#define pfk  printf("fuck\n")

/*  define */
const int inf = 9999999;
int son[1<<20],pa[1<<20];
struct node{
    string name;
    int dead,cost;
}work[250];
int dp[1<<20],n,Time[1<<20];
int dfs(int s){
    if(~dp[s]) return dp[s];
    //cout<<s<<endl;
    int ans=inf,ts,res,pos;
    for(int i=n-1;i>=0;i--){
        ///pfk;
        //cout<<s<<endl;
        if((s>>i)&1){
           // pfk;
            ts=s&~(1<<i);res=dfs(ts);
            int dif=Time[ts]+work[i].cost-work[i].dead;
            if(dif<=0 && res<ans){ans=res;pos=i;son[s]=ts;pa[s]=i;}
            else if(dif>0 && res+dif<ans){ans=res+dif;pos=i;son[s]=ts;pa[s]=i;}
        }
    }
    return dp[s]=ans;
}
int cnt;
void print(int x){
    if(x==0) return ;
    print(son[x]);
    cout<<work[pa[x]].name<<endl;

}
void getTime(){
    for(int s=0;s<1<<n;s++){
        for(int j=0;j<n;j++){
            if(s&(1<<j))
                Time[s]+=work[j].cost;
        }
    }
}
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=0;i<n;i++)
            cin>>work[i].name>>work[i].dead>>work[i].cost;
        clr1(dp);clr(Time);cnt=0;dp[0]=0;
        getTime();
        cout<<dfs((1<<n)-1)<<endl;
        print((1<<n)-1);
    }
    return 0;
}
/*
2
3
Computer 3 3
English 20 1
Math 3 2
*/



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值