20220728 dp训练

时间安排

8:00—8:20 读题 T1感觉比较容易,比较平缓的dp。T2的30pts比较好拿。T3这不是树形dp吗!可惜我不会TwT
8:20—8:50 先从T1入手,状态设置的二维,虽然正解也是二维,但是我太菜了没想到,调不出来后果断放弃
8:50—9:10 把T2的30pts的部分枚举了一下
9:10–9:40 T3试着写了一下树形dp,然并卵挂了,就写了个菊花图和链形的分
9:40–10:00 又确定了一下T2的几种状况
最终得分:0+20+10=30
蓁拉跨啊…

题目分析

T1 [USACO14DEC]Marathon S

Description
Unhappy with the poor health of his cows, Farmer John enrolls them in an assortment of different physical fitness activities. His prize cow Bessie is enrolled in a running class, where she is eventually expected to run a marathon through the downtown area of the city near Farmer John’s farm! The marathon course consists of N checkpoints (3 <= N <= 500) to be visited in sequence, where checkpoint 1 is the starting location and checkpoint N is the finish. Bessie is supposed to visit all of these checkpoints one by one, but being the lazy cow she is, she decides that she will skip up to K checkpoints (K < N) in order to shorten her total journey. She cannot skip checkpoints 1 or N, however, since that would be too noticeable. Please help Bessie find the minimum distance that she has to run if she can skip up to K checkpoints. Since the course is set in a downtown area with a grid of streets, the distance between two checkpoints at locations (x1, y1) and (x2, y2) is given by |x1-x2| + |y1-y2|.

在二维平面上有N个点,从(x1,y1)到(x2,y2)的代价为|x1-x2|+|y1-y2|。
求从1号点出发,按从1到N的顺序依次到达每个点的最小总代价。
你有K次机会可以跳过某个点,不允许跳过1号点或N号点。
Input
The first line gives the values of N and K. The next N lines each contain two space-separated integers, x and y, representing a checkpoint (-1000 <= x <= 1000, -1000 <= y <= 1000). The checkpoints are given in the order that they must be visited. Note that the course might cross over itself several times, with several checkpoints occurring at the same physical location. When Bessie skips such a checkpoint, she only skips one instance of the checkpoint – she does not skip every checkpoint occurring at the same location.
Output
Output the minimum distance that Bessie can run by skipping up to K checkpoints. In the sample case shown here, skipping the checkpoints at (8, 3) and (10, -5) leads to the minimum total distance of 4.
Sample Input
5 2
0 0
8 3
1 1
10 -5
2 2
Sample Output
4

分析
参考导弹拦截的定义方式,必须要记录当前在哪个检查点。
修改状态的定义: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 当前位于 第 i 个 检查点, 跳过了 j个 的最优值。
状态转移:
d p [ i ] [ j ] = m i n ( d p [ i − k − 1 ] [ j − k ] + d i s ( i − k − 1 , i ) ) dp[i][j] = min( dp[i-k-1][j-k] + dis(i-k-1,i) ) dp[i][j]=min(dp[ik1][jk]+dis(ik1,i))

#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
int n,k;
int x[maxn],y[maxn];
int dp[maxn][maxn];
long long ans=0;
int main()
{
	freopen("marathon.in","r",stdin);
	freopen("marathon.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
	memset(dp,10,sizeof(dp));
	dp[1][0]=0;
	for(int i=2;i<=n;i++)
	{
		dp[i][0]=dp[i-1][0]+abs(x[i]-x[i-1])+abs(y[i]-y[i-1]);
		for(int kk=1;kk<=k;kk++)
			for(int j=1;j<i;j++)
				if(kk>=i-j-1)
					dp[i][kk]=min(dp[i][kk],dp[j][kk-i+1+j]+abs(x[i]-x[j])+abs(y[i]-y[j]));
	}
	cout<<dp[n][k]<<endl;
	return 0;
}

T2 梦境

题目描述
GTW 做了一个梦。
这个梦是这样的,GTW 是一个财主,有一个仆人在为 GTW 打工。
不幸的是,又到了月末,到了给仆人发工资的时间。但这个仆人很奇怪,它可能想要至
少 x 块钱,并且当 GTW 凑不出恰好 x 块钱时,它不会找零钱给 GTW。
GTW 知道这个 x 一定是 1~n 之间的正整数。当然抠门的 GTW 只想付给它的仆人恰好 x 块
钱。但 GTW 只有若干的金币,每个金币都价值一定数量的钱(注意任意两枚金币所代表的钱
一定是不同的,且这个钱的个数一定是正整数)。GTW 想带最少的金币,使得对于任意 x,
都能恰好拼出这么多钱。并且 GTW 想知道有多少携带金币的方案总数。
具体可以看样例。
输入格式(dream.in)
第一行一个数 n,如题意所示。
输出格式(dream.out)
输出两个数,第一个数表示 GTW 至少携带的金币个数,第二数表示方案总数。
输入样例
6
输出样例
3 2
样例解释
GTW 需要至少带 3 枚金币,有两种方案,分别是{1,2,3},{1,2,4}来恰好得到任意的 1~n 之
间的 x。
输入样例 2
10
输出样例 2
4 8
数据范围
对于 30%的数据 n<=10。
对于 60%的数据 n<=100。
对于 100%的数据 n<=1000。

分析
第一问用 l o g 2 ( n ) + 1 log2(n)+1 log2n+1即可
第二问用dfs或者dp都可做

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010; 
int n,nn;
int tot=0,ans=0;
int dp[3][maxn][maxn];
int main()
{
	freopen("dream.in","r",stdin);
	freopen("dream.out","w",stdout);
	cin>>n;
	nn=n;
	for(int i=1;i<=nn;i*=2)
	{
		tot++;
		nn-=i;
	}
	if(nn) tot++;
	dp[1][1][1]=1;
	for(int i=1;i<tot;i++)
	{
		for(int j=1;j<=n;j++)
		 for(int k=1;k<=n;k++)
	      if(dp[1][j][k])
	       for(int kk=k+1;kk<=j+1;kk++)
	        dp[2][min(n,j+kk)][kk]+=dp[1][j][k];
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++)
			{
				dp[1][j][k]=dp[2][j][k];
				dp[2][j][k]=0;
			}
		}
	}
	for(int i=1;i<=n;i++) ans+=dp[1][n][i]; 
	cout<<tot<<' '<<ans<<endl;
	return 0;
}

T3 树上距离

懒惰的温温今天上班也在偷懒。盯着窗外发呆的温温发现,透过窗户正巧能看到一棵 n
个节点的树。一棵 n 个节点的树包含 n-1 条边,且 n 个节点是联通的。树上两点之间的距
离即两点之间的最短路径包含的边数。
突发奇想的温温想要知道,树上有多少个不同的点对,满足两点之间的距离恰好等于 k。
注意:(u, v)和(v, u)视作同一个点对,只计算一次答案。
Input
第一行两个整数 n 和 k。
接下来 n-1 行每行两个整数 ai, bi,表示节点 ai 和 bi 之间存在一条边。
1 ≤ k ≤ 500
2 ≤ n ≤ 500 for 40%
2 ≤ n ≤ 50000 for 100%
Output
输出一个整数,表示满足条件的点对数量。
Examples
input
5 2
1 2
2 3
3 4
2 5
output
4
input
5 3
1 2
2 3
3 4
4 5
output
2
分析
需要推导一个公式
把距离给分一下,就变成了两部分,用乘法原理
a n s + = f [ n o w ] [ k − j ] ∗ f [ e d g e [ i ] . y ] [ j − 1 ] ans+=f[now][k-j]*f[edge[i].y][j-1] ans+=f[now][kj]f[edge[i].y][j1]
到now距离为k-j的所有点,到edge[i].y距离为j-1的所有点
再更新一下
f [ n o w ] [ j + 1 ] + = f [ e d g e [ i ] . y ] [ j ] f[now][j+1]+=f[edge[i].y][j] f[now][j+1]+=f[edge[i].y][j];

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,k,len=0;
int lk[maxn];
long long f[maxn][510];
long long ans=0;
struct lx
{
	int nxt,y;
}e[maxn*2];
void add(int a,int b)
{
	e[++len].nxt=lk[a];
	lk[a]=len;
	e[len].y=b;
}
void init()
{
	cin>>n>>k;
	int x,y;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
}
void dfs(int now,int fa)
{
	f[now][0]=1;
	for(int i=lk[now];i;i=e[i].nxt)
	{
		if(e[i].y==fa) continue;
		dfs(e[i].y,now);
		for(int j=1;j<=k;j++) ans+=f[now][k-j]*f[e[i].y][j-1];
		for(int j=0;j<k;j++) f[now][j+1]+=f[e[i].y][j];
	}	
}
int main()
{
	freopen("distance.in","r",stdin);
	freopen("distance.out","w",stdout);
	init();
	dfs(1,1);
	cout<<ans<<endl;
	return 0;
}

反思&总结

每一题先打出暴力,时间分配要合理

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值