记录week13的作业后三道题

作业:
C题:
免费套餐:
题目类似一座树塔,在0-10范围内,某一秒会在若干个位置有馅饼,然后我们要求能接住的最多的馅饼。
首先我们构建一个二维数组,存储某个时间,某个位置的馅饼数目。**dp[t][x]**表示在t时刻,x位置有dp[t][x]个馅饼。
然后总结状态方程:
dp[t][x]=max(dp[t-1][x],dp[t-1][x-1],dp[t-1][x+1])+dp[t][x]
然后在写的时候发现:他不对,因为你初始时在5位置,从t=1开始,无法判断他的来源是否合法,也就是可能会出现t=1时刻,x位置属于0-3或者7-10。但是根据规则,一秒只能移动一个单位长度,所以不合法。
于是倒着看,从最后一个时刻往前数,在中间经过n个状态之后,最后一个时刻可能在任意位置。而遍历到开始的时刻的时候,由于初始化的DP矩阵中其他位置都是0,所以不影响判断,只需要输出dp[0][5]即可。
相应的状态转移方程如下:
dp[i][j]+=max(max(dp[i+1][j],dp[i+1][j-1]),dp[i+1][j+1]);

代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
int m;
int dp[100010][12];

int main(){
int x,t;
while(cin>>m)
{
	memset(dp,0,sizeof(dp));
	if(m==0)break;
	int maxt=0;
	for(int i=0;i<m;i++)
	{ 
	  cin>>x>>t;
	  dp[t][x]++;
	  maxt=max(maxt,t);
	}
	//dp[t][x]=max(dp[t-1][x],dp[t-1][x-1],dp[t-1][x+1])+dp[t][x]

	dp[0][5]=0; 
for(int i=maxt-1;i>=0;i--)	
{	
dp[i][0]+=max(dp[i+1][0],dp[i+1][1]);
  for(int j=1;j<=9;j++)	
  {
  dp[i][j]+=max(max(dp[i+1][j],dp[i+1][j-1]),dp[i+1][j+1]);
  }
dp[i][10]+=max(dp[i+1][9],dp[i+1][10]);
}	
cout<<dp[0][5]<<endl;
	
}

return 0;
}

D题:
经典的题目:没有上司的舞会。这是一道树形DP,因为输入的是一棵树的边。开始我们用前向星构建一棵树,然后根据出度和入度找出他的根节点,并且给叶子节点赋值。
令f[i][0/1]为以i节点为根的能得到的最大快乐值。第二维表示i这个点算不算进去。
于是得到递归表达式:

f[i][0]=max( f[x][1],f[x][0]);//上司不参加,下属可以参加也可以不参加
f[i][1]=w[i]+f[x][0];//上司参加,下属只能不参加

于是带入DFS进行递归处理即可。
代码:

#include <iostream>
using namespace std;
#define M 6005 
struct node
{
	int nxt,to,val;
}edge[M*2];
int head[M*2];int cnt;
void add(int x,int y)
{
	edge[++cnt].to=y;
	edge[cnt].nxt=head[x];
	head[x]=cnt;
}
int w[M],ru[M],chu[M];
int rt;
int dp[M][M];
//以u为根节点的子树 最大是多少   fa记录他爹 
void dfs(int u,int fa)
{
	dp[u][1]=w[u];
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)continue;
		dfs(v,u);
		dp[u][1]+=dp[v][0];
		dp[u][0]+=max(dp[v][1],dp[v][0]);
	}
}
int main()
{
  
  	int n,u,v;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>w[i];
	for(int i=1;i<n;i++)
	cin>>u>>v,add(u,v),add(v,u),chu[u]++,ru[v]++;
	cin>>u>>v;
	for(int i=1;i<=n;i++)
	{
		if(chu[i]==0)rt=i;
		if(ru[i]==0)dp[i][1]=w[i];
	}
	dfs(rt,0);
	cout<<max(dp[rt][0],dp[rt][1])<<endl;
	return 0;
}

E题
Max Sum of Max-K-sub-sequence:
单调队列处理的题目,类似之前做的滑动窗口。这道题是一个环,我们在这个环上,找出一个连续的不超过K长度的序列,使这个序列的每个元素之和最大。
首先定义一个双端队列deque,作为单调队列使用。我们为了表示序列的和,降低其复杂度,使用前缀和表示区间和。即构造前缀和,使得S[i]-S[j]=a[j+1]+a[j+2]+…+a[i-1]+a[i].
按照题意:我们求要求出 [i-K,i] 的区间上的最小的那个前缀和s[j]使得S[i]-S[j]最大,即令区间 [j+1,i] 内的各个元素的和最大。为了解决环的问题,我们将数组扩大两倍。N个元素,区间为K我们只需要遍历到N+K-1即可包含环中的所有情况。

代码:

#include<bits/stdc++.h>
#include<stdio.h>
#include<deque>
#define inf 0x3f3f3f3f
using namespace std;
int T,N,K;
long long int b[2*100010],sum[2*100010];




int main(){
cin>>T;
while(T--)
{
	deque<int>q;q.clear();
	int l,r;
	long long int ans=-inf;
	memset(b,0,sizeof(b));memset(sum,0,sizeof(sum));
	cin>>N>>K;
	for(int i=1;i<=N;i++){scanf("%lld",&b[i]);b[N+i]=b[i];}
	for(int i=1;i<=2*N;i++)sum[i]=sum[i-1]+b[i];
	
	for(int i=1;i<=N+K-1;i++)
	{
		while(!q.empty()&&sum[q.back()]>sum[i-1])
		q.pop_back();
		q.push_back(i-1);//!!!!!!前缀和相减  sum[j]-sum[i-1] 
		while(!q.empty()&&q.front()+K<i)
		q.pop_front();
		if(ans<sum[i]-sum[q.front()]){l=q.front()+1;r=i;}
		ans=max(ans,sum[i]-sum[q.front()]); 
	}
	printf("%lld %d %d\n",ans,l>N?l-N:l,r>N?r-N:r);
}



return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值