AtCoder Educational DP Contest

本来想等写完来写总结的..........

还是没写完啊。

前面的题大多比较水....后面倒是很多难题没写。不过通过这场DP Edu,也让我发现我连背包都写不清楚....

以前对动态规划这部分有点逃避()现在要好好练习了啊。

感谢 chy 在高一讲的dp基础技巧。

A

基础递推题啦QAQ

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN (int)(1e5+233)
#define MAXH (int)(1e4+233)
int f[MAXN],h[MAXN];
 
int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&h[i]);
	f[2]=abs(h[2]-h[1]);
	for (int i=3;i<=n;i++)
		f[i]=min(abs(h[i]-h[i-1])+f[i-1],abs(h[i]-h[i-2])+f[i-2]);
//	for (int i=1;i<n;i++) printf("%d ",f[i]);
	printf("%d\n",f[n]);
	return 0;
}

B

还是基础递推...

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define MAXN (int)(1e5+233)
#define MAXH (int)(1e4+233)
int f[MAXN],h[MAXN];
 
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&h[i]);
	f[2]=abs(h[2]-h[1]);
	for (int i=3;i<=n;i++)
	{
		f[i]=abs(h[i]-h[i-1])+f[i-1];
		for (int j=i-2;j>=i-k;j--)
			if (j>0) f[i]=min(f[i],abs(h[i]-h[j])+f[j]);
	}
		
//	for (int i=1;i<n;i++) printf("%d ",f[i]);
	printf("%d\n",f[n]);
	return 0;
}

I - Coins

有\(N\)枚硬币,第\(i\)枚硬币抛出后正面朝上的概率为\(p\),反面朝上的概率为\(1-p\)

扔完所有硬币,求正面朝上的银币数比反面朝上的银币数多的概率。

设\(f[i][j]\)表示抛\(i\)枚硬币,有\(j\)枚朝上的概率。

那么就有\(f[i][j]=f[i-1][j]*(i-p[i])+f[i-1][j-1]*p[i]\)

然后对于每个\(f[n][i](i>=(n+1)/2)\)统计就好了对吧(

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define MAXN 3021
using namespace std;
int n;
double p[MAXN],f[MAXN][MAXN];
double jt[MAXN];

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%lf",&p[i]);
	f[1][0]=1.000-p[1]; f[1][1]=p[1];
	for (int i=2;i<=n;i++)
		for (int j=0;j<=i;j++)
			if (j>0)
				f[i][j]=f[i-1][j]*(1.00-p[i])+f[i-1][j-1]*p[i];
			else f[i][j]=f[i-1][j]*(1.00-p[i]);
	double ans=0;
	for (int j=(n+1)/2;j<=n;j++) ans+=f[n][j];//,printf("%d %d : %lf\n",n,j,f[n][j]);
	printf("%.10lf\n",ans);
	return 0;
}

J - Sushi

设\(f[a][b][c][d]\)为剩余\(a/b/c/d\)盘\(0/1/2/3\)的期望次数

那么有

\[f[a][b][c][d]= 1 + a/n f[a][b][c][d] + b/n f[a+1][b-1][c][d] + c/n f[a][b+1][c-1][d] + d/n f[a][b][c+1][d-1] \]

\[(n-a)/n f[a][b][c][d]= 1 + b/n f[a+1][b-1][c][d] + c/n f[a][b+1][c-1][d] + d/n f[a][b][c+1][d-1] \]

\[f[a][b][c][d]= n/(n-a) + b/(n-a) f[a+1][b-1][c][d] + c/(n-a) f[a][b+1][c-1][d] + d/(n-a) f[a][b][c+1][d-1] \]

发现\(a=n-b-c-d\)

替换一下状态w

\[f[b][c][d]=n/(b+c+d) + b/(b+c+d) f[b-1][c][d] + c/(b+c+d) f[b+1][c-1][d] + d/(b+c+d) f[b][c+1][d-1] \]

发现d是递增的,拿它当最外层阶段,c当次外层

最后答案是f[sum1][sum2][sum3]

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define MAXN 321
using namespace std;
int n;
double f[MAXN][MAXN][MAXN];

int main()
{
	scanf("%d",&n);
	int sum1=0,sum2=0,sum3=0;
	for (int i=1,a;i<=n;i++)
	{
		scanf("%d",&a);
		if (a==1) sum1++;
		else if (a==2) sum2++;
		else sum3++;
	}
	for (int d=0;d<=sum3;d++)
		for (int c=0;c<=sum2+sum3;c++)
			for (int b=0;b<=n;b++)
			{
				if (!(b||c||d)) continue;
				f[b][c][d]=1.00*n/(1.00*b+1.00*c+1.00*d);
				if (b>=1) f[b][c][d]+=b*1.0/(b*1.0+c*1.0+d)*(double)f[b-1][c][d];
				if (c>=1&&b+1<=n) f[b][c][d]+=c*1.0/(b*1.0+c*1.0+d)*(double)f[b+1][c-1][d];
				if (d>=1&&c+1<=sum2+sum3) f[b][c][d]+=d*1.0/(b*1.0+c*1.0+d)*(double)f[b][c+1][d-1];
			}
	/*
	for (int i=0;i<=sum1;i++)
		for (int j=0;j<=sum2;j++)
			for (int k=0;k<=sum3;k++)
				printf("%d %d %d : %lf\n",i,j,k,f[i][j][k]);
	*/
	printf("%.10lf\n",f[sum1][sum2][sum3]);
	return 0;
}
上一篇:AtCoder Beginner Contest 232题解


下一篇:AtCoder abc231_d(或者说……UnionFind?)