您的位置:首页 > 其它

HDU1074 Doing Homework

2013-04-13 11:29 232 查看
题目大意:给出n分homework,每份homework有截止时间 以及需要做多少天,一份homework超出截止时间一天就罚一分,问怎么安排do homework使得罚分最少

 

思路:第一次状态压缩DP.也很纠结...这次跟文东,鑫固讨论了下,多理解了下下

因为总共有15份的homework,所以就有15!的安排方案,暴力肯定超时,但是如果用2进制来dp的话,2^15的状态数,可以接受.

这就是所谓的状态压缩DP,就是利用0辅助状态由小数推导大数,从而实现状态转移:

当前的状态由前一状态得出,因为罚分能更新的则更新.

感觉就是暴力出来的dp.

这里的2进制用得妙,因为后面的状态根据前面的状态推出来跟2进制逐渐变大是类似的.

 

AC Program:(无注释版)

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<map>
#define inf 35000
#define oo 1000000000
using namespace std;
struct node
{
string name;
int fday;
int lday;
}hw[20];
struct point
{
int pre;//前缀
int rescore;//罚分
int ttime; //当前时间
point()
{
pre=-1;
rescore=oo;//求最小值就赋值大值
ttime=0;
}
};
void pn(int state,point dp[])
{

if(state==0)
return;
int pre2=dp[state].pre;
pn(pre2,dp);
int work=(state^pre2),cnt=0;
while(work!=1)
{
work=(work>>1);
cnt++;
}
cout<<hw[cnt].name<<endl;
}
int main()
{
int test,n;
cin>>test;
while(test--)
{
cin>>n;
for(int i=0;i<n;i++)
cin>>hw[i].name>>hw[i].fday>>hw[i].lday;
int state=(1<<n)-1,tmp,tmptime;
point dp[inf];
dp[0].rescore=0;
dp[0].ttime=0;
for(int i=0;i<state;i++)
{
for(int j=0;j<n;j++)
{
tmp=(1<<j);
if((i&tmp)==0)
{
int tmpstate=i|tmp;
int rs=dp[i].ttime+hw[j].lday-hw[j].fday;
if(rs<0)
rs=0;
rs+=dp[i].rescore;
int tt=dp[i].ttime+hw[j].lday;
if(dp[tmpstate].rescore>rs)
{
dp[tmpstate].pre=i;
dp[tmpstate].rescore=rs;
dp[tmpstate].ttime=tt;
}
}
}
}
cout<<dp[state].rescore<<endl;
pn(state,dp);
}
//system("pause");
return 0;}


 

 

AC Program(注释版):

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<map>
#define inf 35000
#define oo 1000000000
using namespace std;
struct node
{
string name;
int fday;
int lday;
}hw[20];
struct point
{
int pre;//前缀
int rescore;//罚时
int ttime; //当前时间
point()
{
pre=-1;
rescore=oo;//求最小值就赋值大值
ttime=0;
}
};

void pn(int state,point dp[])
{

if(state==0)//0是辅助状态,并且是开始状态
return;
int pre2=dp[state].pre;
pn(pre2,dp);
int work=(state^pre2),cnt=0;
while(work!=1)
{
work=(work>>1);
cnt++;
}
cout<<hw[cnt].name<<endl;
}
int main()
{
int test,n;
cin>>test;
while(test--)
{
cin>>n;
for(int i=0;i<n;i++)
cin>>hw[i].name>>hw[i].fday>>hw[i].lday;
int state=(1<<n)-1,tmp,tmptime;
point dp[inf];
dp[0].rescore=0;//开始为0很符合意思.
dp[0].ttime=0;
for(int i=0;i<state;i++)//i不会到state,利用滚动可以dp取到state.这就是0的作用
{
for(int j=0;j<n;j++)//注意1是左移n-1次
{
tmp=(1<<j);
//检索当前这个工作做了没有,如果已经做了,则跳过
//如果没有做的话,则加入新的工作去更新当前的dp[i]这个状态.
//cout<<"tmp i&tmp "<<tmp<<" "<<(i&tmp)<<endl;
if((i&tmp)==0)//新状态,记录下来
{
//没有做过j,就把j加进去.加进来的状态不管加没加过分数都
//直接比较看大小,这是初始化的功劳
//感觉就是用前面的去推倒后面的,这就是,2进制的妙
//用小数的状态去推导大数的状态
int tmpstate=i|tmp;//利用或逻辑运算符加进状态

int rs=dp[i].ttime+hw[j].lday-hw[j].fday;//当前i|j这个状态的罚分
if(rs<0)
rs=0;
//if(i!=0) dp[0]的赋值省了一部分的功夫
rs+=dp[i].rescore;//当前状态最后的罚分=(前一状态的罚分+当前的罚分)
int tt=dp[i].ttime+hw[j].lday;//i|j这个状态的当前时间
if(dp[tmpstate].rescore>rs)
{
dp[tmpstate].pre=i;//前驱不能随便改,不然就不能字典序了
dp[tmpstate].rescore=rs;
dp[tmpstate].ttime=tt;
}

}
}
}
cout<<dp[state].rescore<<endl;
pn(state,dp);
}
//system("pause");
return 0;}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: