题目描述
有一块大小为n行2列的板子,每个位置可能是一个小球,用
′
o
′
'o'
′o′表示,可能是障碍物,用
′
x
′
'x'
′x′表示,也可能空无一物,用
′
.
′
'.'
′.′来表示。
每个小球可以向左向右或者向下移动,但是不能向上移动,或者和某个小球重叠,也不能越出板子。
每个小球向下移动一个单位,牛牛可以获得一分。
牛牛想知道对于某个开始状态,能得到的最大分数是多少。
输入描述:
第一行输入一个整数n,表示板子的行数。
随后n行,每行一个长度为2的字符串,如题意所示。
设有k个小球。
对于 20 20 20%的数据有 n ≤ 3 n≤3 n≤3。
另有 20 20 20%的数据 k ≤ 10 k≤10 k≤10。
对于 60 60 60%的数据有n 1000 n ≤ 1000 1000n≤1000 1000n≤1000。
对于 100 100 100%的数据有 1 ≤ n ≤ 300000 1≤n≤300000 1≤n≤300000。
输出描述:
一行一个整数表示答案。
示例1
输入
10
oo
.o
.x
x.
…
oo
.o
x.
…
x.
输出
12
说明
最终结果为:
…
oo
ox
x.
…
…
…
x.
oo
xo
解题思路
当时打了个暴搜,结果测的大样例错了,没时间修改了,不抱希望的交了上去,结果尽然骗到了40分!
正解:
首先核心思想是贪心。反向输入图形,把第行当作第行,现在的目标就是尽可能的去让球去填满上面的空。但是,有三种情况是会封死路的:
看看代码,然后读完下面的解释你就懂了。
- F数组下标是递增的,代表目前除了x的个数; F数组的值代表这个不是x的字符是第几行的。
顺着下来,上去的球就也相当于点了。 - p就是下标.
if(a[i][1]!=1)f[p++]=i; if(a[i][2]!=1)f[p++]=i;
- t为全部空位(包括点和已经上去的球)已被球占用了的个数,也同样作为的下标。
if(a[i][1]==2)ans+=f[t++]-i; if(a[i][2]==2)ans+=f[t++]-i;
ans加上当前行减去目前存在空位最上面的行,t继续自增. - 最后,就是当出现三种拦路的情况,直接让 p , t p,t p,t 都清即可。相当于就是开辟了个新的分区,啥都清了。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
long long n,ans,p,t,a[400000][3],f[1000000];
char c;
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=2; j++)
{
cin>>c;
if(c=='o')
a[i][j]=2;
if(c=='x')
a[i][j]=1;
if(c=='.')
a[i][j]=0;
}
}
for(int i=n; i>0; i--)
{
if(a[i][1]!=1)f[p++]=i;
if(a[i][2]!=1)f[p++]=i;
if(a[i][1]==2)ans+=f[t++]-i;
if(a[i][2]==2)ans+=f[t++]-i;
if((a[i][1]==1&&a[i][2]==1)||(a[i][1]==1&&a[i-1][2]==1)||(a[i-1][1]==1&&a[i][2]==1))
p=t=0;
}
printf("%lld",ans);
}