8.17 (军训DAY 1)

今天开始学习DP  这是个奇妙的过程 。从磕入门开始。

呃...导弹拦截和数字三角形还是比较好理解的

然后就是导弹拦截的练习题合唱队形。

   LUOGU1091 合唱队形

题目描述:N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形

思路和导弹拦截差不多  一遍正搜一遍反搜  正搜:以第i人为最高的中间人  则f1[i]为他左边需要删掉的人。反搜 相反 f2[i]为他右边需要删掉的人 然后再把两个值加起来就为第i人为最高时需要出列的同学数量 。将最小值输出即可。我的代码如下:
#include<bits/stdc++.h>
using namespace std;
int f1[200]={0},f2[200]={0},f[200]={0};
int main()
{
 
 int i,j,n,min;
 scanf("%d",&n);
 int a[n+1]; 
 bool flag;
 for(i=1;i<=n;i++) scanf("%d",&a[i]);
 for(i=2;i<=n;i++)
  {
  f1[i]=i;
  flag=true;
  for(j=1;j<i;j++)
    if ((a[j]<a[i])&&((f1[j]+i-j-1)<f1[i]))  f1[i]=f1[j]+i-j-1,flag=false;
  if(flag) f1[i]=i-1;
  }
  for(i=n-1;i>=1;i--)
  {
  f2[i]=n-i+10;
  flag=true;
  for(j=n;j>i;j--)
    if ((a[j]<a[i])&&((f2[j]+j-i-1)<f2[i]))  f2[i]=f2[j]+j-i-1,flag=false;                                            
  if(flag) f2[i]=n-i;
  }
  min=100;
  for(i=1;i<=n;i++) 
   {
   f[i]=f1[i]+f2[i];
   if(f[i]<min) min=f[i];
   }
  cout<<min;
 return 0;
}


看完了动归基础  先做一题练习题

LUOGU1280 尼克的任务

题目描述尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。尼克的一个工作日为N分钟,从第一分钟开始到第N分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完戍,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第P分钟开始,持续时间为T分钟,则该任务将在第P+T-1分钟结束。写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。

这题是把我难坏了......先是按照之前的思路 。前i个任务的最优值。循环到后面发现多个任务重复在同一时间将会很难处理.....试了很多办法也没办法处理这个问题....也就是顺推推不出来(反正我没推出来= =)于是按照舒婧大佬的思路,从最后一个时间点开始  如果这个时间点不是某个任务的开始点则视为休息。用S【i】存储  如果有任务则S【i】=S【结束的时间点】+1。多个任务重复在一个时间点就比哪个能休息的时间长.代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,k,s[10010]={0};
struct work
{
	int begin,end,time;
};
work w[10010];
void init()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d",&w[i].begin,&w[i].time); 
		w[i].end=w[i].begin+w[i].time-1;
	}
	return;
}

bool mycmp(work x,work y)
{
	return(x.begin<y.begin);
}
	
int main()
{
	init();
	sort(w+1,w+k+1,mycmp);
	for(int i=n;i>=1;i--)
	{
		bool flag=false;
	    for(int j=1;j<=k;j++)
	    if(w[j].begin==i )s[i]=max(s[i],s[w[j].end+1]),flag=true;
	    if(flag==false)s[i]=s[i+1]+1;
	}
	printf("%d",s[1]);
	return 0;
}

.后面就是一些资源分配类的题目  两个例题分别是机器分配P1263和复制书稿P1264 花了一会时间也算是理解了

嗑完入门就是背包问题了  嗑完了入门01背包就比较容易理解了  然而01背包洛谷里面的练习....

第一题采药

LUOGU1048 采药

题目描述辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”如果你是辰辰,你能完成这个任务吗?

第一题 还算是简单 和01背包差别不大  按照01背包的思路很容易
第二题

LUOGU1466 集合 Subset Sums

题目描述对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,每个子集合的所有数字和是相等的:{3} 和 {1,2}这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数) 如果N=7,有四种方法能划分集合{1,2,3,4,5,6,7},每一种分法的子集合各数字和是相等的给出N,你的程序应该输出划分方案总数,如果不存在这样的划分方案,则输出0。程序不能预存结果直接输出(不能打表)。

 这是一个神奇的题目....很难受  这让我明白了学dp 从看不懂到看懂 看懂到理解 理解到应用是艰难的过程.....这道题看了题解我也没能理解他的状态转移方程(f[j]=f[j]+f[j-i])是什么原理.....反正思考了很久  求助了刘老师看了一些大佬 的笔记文章还是有点迷迷糊糊 不能理解他的原理...   后来可能是多年攒下的rp爆发了  灵光一闪  想到了这是特殊的01背包....不过是特殊到能把数字代到01背包里面的这个代码:
procedure ZeroOnePack(cost,weight)
    for v=V..cost
        f[v]=max{f[v],f[v-cost]+weight}
for i=1..N
    ZeroOnePack(c[i],w[i]);
一代 就得到了题解里面的:
 for(int i=1;i<=n;i++)
        for(int j=t;j>=i;j--)
            f[j]=f[j]+f[j-i];
然后再理解就不是那么难了。

然后就是02背包.02背包和01还是有区别的。同一种物体可以重复用 那么有些价值高的就可以多拿了  而且这就不用像01背包一样怕物体重复拿而逆推。直接顺还解决了重复拿的问题......  

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值