hdu1074状态压缩

很明显的状态压缩题目。当然了如果他的意识只有是求最小值没有要求输出那个输出的顺序,那么直接用背包,甚至是贪心都是可以做出来的,但是要求顺序这个背包的话没有办法吧数据记录下来。所以用二进制压缩。

所谓的二进制压缩就是用二进制来表示事情的完成程度和状况,比如1,就是0000001表示第一个作业做了,别的都没做,101表示1,3做了,别的没做。。。一直到2^n-1也即是11111111111111111.。。。。表示所有完成而n<=15不是很大完全可以做出来。思想和背包一样就是我们对于任何一个数1<=m<n那么作业i(i>.=0)如果我们选择了作业i那么作业i就应该做了,也就是相应的位置上m的二进制数是一个1,那么m&2^j就是一个大于0的数字,这样我们就可以用m-2^j来推导m的状态。比如现在m=1110001101而j=3,那么2^3=1000所以同或以后就是1110000101,也就是在1110000101的基础上选择j这个东西,遍历一遍所有的可以的j值就可以找到最优的情况。这样到最后111111111111111111111.。。。。就可以全部求出。

最后输出顺序是倒置的所以用递归,栈,哪怕是数组(倒置输出)也是可以的。这个不是重点就不多讲。

#include<iostream>
using namespace std;
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<cstdlib>
const int INF=99999999;//卡的比较严的话还是用0x7fffffff(int最大)比较好
const int Max=(1<<15)+10;相当于2^15+10;不过速度更快
struct Hom
{
    char name[105];
    int dead;
    int time;///要花费的时间。
}Dp[20];
struct hom
{
    int score;///标记每一个阶段扣分最少的情况
    int pre;标记在他之前的一个作业
    int time;标记最完作业最少的时间
}dp[Max];
void output(int x)
{
    if(!x)return ;
    output(x-(1<<dp[x].pre));
    cout<<Dp[dp[x].pre].name<<endl;
}
int main()
{
    int T,N;
    int i,j,k;
    cin>>T;
    while(T--)
    {
        cin>>N;
        memset(dp,0,sizeof(dp));
        memset(Dp,0,sizeof(Dp));
        for(i=0;i<N;i++)
            scanf("%s%d%d",&Dp[i].name,&Dp[i].dead,&Dp[i].time);
        int temp=1<<N;
        for(i=1;i<temp;i++)
        {
            dp[i].score=INF;
            for(j=N-1;j>=0;j--)
            {
                int M=1<<j;
                if(M&i)
                {
                    int reduce=dp[i-M].time+Dp[j].time-Dp[j].dead;///Dp表示最原始的科目的数据。
                    if(reduce<0)reduce=0;
                    if(reduce+dp[i-M].score<dp[i].score)
                    {
                        //cout<<"hao"<<endl;
                        dp[i].score=reduce+dp[i-M].score;
                        dp[i].time=dp[i-M].time+Dp[j].time;
                        dp[i].pre=j;
                    }
                }
            }
        }
     cout<<dp[temp-1].score<<endl;
     output(temp-1);
    }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值