HDU 1074
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1024
题意:给定一个数组求其分成m个不相交的子段和最大问题;
做法(这里是看kuangbin的做法的)
注意:这里的做法有点像背包的01数组,但这里更巧妙的是,在每次滚动的时候顺便将j-1的前i-1的最大值求出。就可以直接转移了
具体操作;
dp[i]=max(dp[i-1]+a[i],mmdp[i-1]+a[i]);
mmdp[i-1]=ans;
ans=max(ans,dp[i]);
#include
#include
#include
#include
using namespace std;
int a[1000010];
int dp[1000010];
int mmdp[1000010];
int main()
{
int n,m;
while(scanf("%d%d",&m,&n)!=EOF)
{
int ans;
for(int i=1;i<=n;++i)
{
dp[i]=0;
mmdp[i]=0;
scanf("%d",&a[i]);
}
dp[0]=mmdp[0]=0;
for(int j=1;j<=m;++j)
{
ans=-0x3fffffff;
for(int i=j;i<=n;++i)
{
dp[i]=max(dp[i-1]+a[i],mmdp[i-1]+a[i]);
mmdp[i-1]=ans;
ans=max(ans,dp[i]);
}
}
printf("%d\n",ans);
}
return 0;
}
题意:给出一些作业,每种作业有两个参数(截止日期,完成作业需要多久)每超过截止日期一天扣一分,问最少扣多少分,如果答案相同输出字典序最小的.(题目的输入顺序是字典序从小到大)
傻傻的贪心贪了特别久,各种排序呜呜呜~~~~(>_<)~~~~
看了题解:发现尼玛。。。状压DP完全没想过。
思路:一个n位的二进制数,每一位表示第几个作业是否做完.然后对于每一个数枚举位数,判断是否能将扣的分变小(有点像松弛操作).枚举的时候倒叙枚举这样才字典序最小.同时每次松弛之后要把pre记录下来方便后面的输出!
#include
#include
#include
#include
using namespace std;
const int maxn=1<<15;
struct node
{
char name[110];
int dead,need;
}home[30];
struct DP
{
int cost;
int time;
int pre;
}dp[maxn+10];
void dfs(int n)
{
if(!n) return;
int x=dp[n].pre;
dfs(n-(1<=0;--j)
{
int temp=1<dp[i-temp].cost+reduce)
{
dp[i].cost=dp[i-temp].cost+reduce;
dp[i].pre=j;
dp[i].time=dp[i-temp].time+home[j].need;
}
}
}
}
printf("%d\n",dp[k-1].cost);
dfs(k-1);
}
return 0;
}
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1176
中文题不解释了。
自己做的时候是正向考虑的也是用dp,因为每次都要判断这个点是否能到达,还有边界情况然后就写炸了.看了别人的博客,都是从结尾往回dp,这样就不需要再去找答案,且不需要考虑这个点能不能走到,或者什么时候能走到的问题。边界的问题直接把坐标左移1,就可以了(哦,我真是蠢)!!.
#include
#include
#include
#include
using namespace std;
const int maxn=100005;
int dp[15][maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
memset(dp,0,sizeof(dp));
int maxtime=0;
for(int i=0;i=0;--j)
{
for(int i=1;i<=11;++i)
{
dp[i][j]+=max(dp[i][j+1],max(dp[i-1][j+1],dp[i+1][j+1]));
}
}
printf("%d\n",dp[6][0]);
}
return 0;
}