Contest1078 - NOIP模拟赛190128【应该是19.1.28之前最认真的题解了】

Contest1078 - NOIP模拟赛190128

A:MZOJ1355: 迷宫

在这里插入图片描述

思路

1.n在1~25之间----->尽量不用DFS,需要用到DP
2.要是普通的走迷宫那还好,DP方程很好想,但是这道题比较奇葩的是要维护两个值——距离&&条数,而二维DP只能维护其中其一,所以我们选用三维DP——f(t,i,j)表示从起点到i,j这个点的距离为t路径的条数(注意:t不一定是最小的哦)
3.状态设计及转移:
father的t值等于他儿子的t-1;
father的条数等于他儿子的条数之和
即:
在这里插入图片描述
4.几个小问题。
起点如何设计? f(0,startx,starty)=1即可
如何设计顺序? t在最外层且从1-n*n,其他顺序没什么要求,因为初值只有起点才为1,要是其他乱走,f值也为0,无影响。
如何保证t值最小? 在最后找答案的时候按t值从小到达的顺序遍历,找到第一个f有值的时候说明此时的t最小,然后输出即可。

code

/*	NAME:EPEILONCXL;
	LANG:C++;
	TIME:8:20
	PROB:A
	          \`,""   ,'7"r-..__/ \
          ,'\/`, ,',','    _/   \
         /   \/ 7 / /     (   \ |
        J     \/ j  L______\  / |
        L   __JF"""/""\"\_,    /
        L,-"| O|  | O |  L_  _/
        F   \_ /  \__/   `-  /|
            .-'    `"""    ,' |          _..====.._
            \__/         r"_  A        ,' _..---.._`,
             `-.______,,-L// / \  ___,' ,'_..:::.. `,`,
                      /   / / / 7"    `-<""=:'  '':. \ \
                     |   <,' /  F  . i , \   `,    :C X L
                     |    \,'  /    >X<  |     \   :| |  X
                     \     `._/    ' ! ` |      C  :| |  C
                      \           \     /       |  :C C  |
                     __>-.__   __,-\   |        |  X X   |
                    /     /|   | \  \  \_       | 'L L   |
                   /   __/ |   |  \  \   \      X./ /   L
                  /   |    |   |  |  |   |     L/ /   ,'
                 |    \    |   |  |  |   |    // / _,'
        _________|     |   |   |  |  |   |   //_/-'
        \   __   |    /    |   | /   |   |  /="
        \\  \_\  \__,' \  /    |/   /    |
        \\\_____________\/     C___X     L
         \\| EPSILONCXL |_____/   (_____/
          \/TEST:19.1.28A/

*/

#include <bits/stdc++.h>
using namespace std;
const int maxn=30;
int mapp[maxn][maxn];
int f[maxn*maxn][maxn][maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int n,endx,endy,startx,starty;
void init()
{
	freopen("A.in","r",stdin);
}

void readdata()
{
	char c;
	scanf("%d\n",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%c",&c);
			if(c!='X')mapp[i][j]=0;
			if(c=='X')mapp[i][j]=1;
			if(c=='S'){startx=i;starty=j;}
			if(c=='E'){endx=i;endy=j;}
		}	
		scanf("\n");
	}
}

void work()
{
	f[0][startx][starty]=1;
	for(int t=1;t<=n*n;t++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(!mapp[i][j])
				{
					for(int k=0;k<4;k++)
					{
						int nx=i+dx[k],ny=j+dy[k];
						if(!mapp[nx][ny] && nx>0 && ny>0 && nx<n+1 && ny<n+1)
						{
							f[t][i][j]=f[t][i][j]+f[t-1][nx][ny];
						}
					}
				}
			}
		}
	}
	
	for(int t=1;t<=n*n;t++)
	{
		if(f[t][endx][endy])
		{
			printf("%d\n%d",t,f[t][endx][endy]);
			break;
		}
	}
}

int main()
{
	init();
	readdata();
	work();
	return 0;
}

B:1356: 最大数列

在这里插入图片描述

题意

从一段区间中选取两段区间,使区间和最大,但是这两段区间不能有交集。

思路

dp[i]表示以i节点为结尾的最大值,不难得出递推方程式:
在这里插入图片描述

难点

如何维护两端不相交的区间的最大值
两遍DP
第一遍DP正向从1~n,维护出dp的值,表示从正方向上以i节点结尾的最大值
第二遍DP反向维护出一个反向的最大区间
下面主要讲一讲如何维护:

以样例为例:

5
-5 9 -5 11 20

1.第一遍DP:正向从1~n,维护出dp的值

void dp_head()
{
1    memset(dp,0,sizeof(dp));
2    for(int i=1;i<=n;i++) 	dp[i]=max(dp[i-1]+a[i],a[i]);
}

维护出dp[i]的值这个都没有什么好多要说的
eg:

dp  0  1  2  3  4  5  
    0 -5  9  4  15 35  

最主要是反向维护:

2.第二遍DP:反向维护出一个反向的最大区间

几个变量的意思:
ans:最终两端区间的和的最大值
flag:当前i的后缀和。也就是相当与一个dp数组
sum:从n~1反向遍历所寻找的1个区间最大值
inf:一个极小值

void dp_back()
{
1	ans=inf,flag=0,sum=inf;
2	for(int i=n;i>1;i--)
3	{
4		if(flag>0)flag+=a[i];
5		else flag=a[i]; 
6		
7		if(flag>sum)sum=flag;
8		if(sum+dp[i-1]>ans)ans=sum+dp[i-1];
9	}
}

代码解释

2~5:从右往左遍历每一个点,并且维护出当前点i至n的区间最大值(也就是dp的任务,只不过dp是从左往右的顺序)
7:更新sum
8:如果后续区间的最大值比以i-1为结尾的前区间的和大于最后的ans,更新ans

几个小细节

2:遍历顺序应该从n至2:只有这样才能保证将一段序列分为两端:

在这里插入图片描述
如果i=1,那么i-1=0,就相当于只有一个区间,且这个区间还是原来的序列数组
8:应该用sum+dp而不应该用flag+dp:flag只是指当前的点最优解而不是指当前i往后的一个区间的最大值

至此,该题我认为的难点和坑已经叙述得差不多了,所以

code

/*	NAME:EPEILONCXL;
	LANG:C++;
	TIME:9:08
	PROB:B
	          \`,""   ,'7"r-..__/ \
          ,'\/`, ,',','    _/   \
         /   \/ 7 / /     (   \ |
        J     \/ j  L______\  / |
        L   __JF"""/""\"\_,    /
        L,-"| O|  | O |  L_  _/
        F   \_ /  \__/   `-  /|
            .-'    `"""    ,' |          _..====.._
            \__/         r"_  A        ,' _..---.._`,
             `-.______,,-L// / \  ___,' ,'_..:::.. `,`,
                      /   / / / 7"    `-<""=:'  '':. \ \
                     |   <,' /  F  . i , \   `,    :C X L
                     |    \,'  /    >X<  |     \   :| |  X
                     \     `._/    ' ! ` |      C  :| |  C
                      \           \     /       |  :C C  |
                     __>-.__   __,-\   |        |  X X   |
                    /     /|   | \  \  \_       | 'L L   |
                   /   __/ |   |  \  \   \      X./ /   L
                  /   |    |   |  |  |   |     L/ /   ,'
                 |    \    |   |  |  |   |    // / _,'
        _________|     |   |   |  |  |   |   //_/-'
        \   __   |    /    |   | /   |   |  /="
        \\  \_\  \__,' \  /    |/   /    |
        \\\_____________\/     C___X     L
         \\| EPSILONCXL |_____/   (_____/
          \/TEST:19.1.28B/

*/


#include <bits/stdc++.h>
using namespace std;
const int inf=-1e9;
const int maxn=100005;
int dp[maxn],a[maxn],ans,flag,sum,n; 
int read()
{
	char ch=getchar();int x=0,f=1;
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void init()
{
	freopen("B.in","r",stdin);
}

void readdata()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
}

void dp_head()
{
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
		dp[i]=max(dp[i-1]+a[i],a[i]);
    }
}

void dp_back()
{
	ans=inf,flag=0,sum=inf;
	for(int i=n;i>1;i--)
	{
		if(flag>0)flag+=a[i];
		else flag=a[i]; 
		
		if(flag>sum)sum=flag;
		if(sum+dp[i-1]>ans)ans=sum+dp[i-1];
	}
}

void work()
{
	dp_head();
	dp_back();
	printf("%d",ans);
}

int main()
{
	init();
	readdata();
	work();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_25845753/article/details/86678597