APIO2020 粉刷墙壁 题解

P6764 [APIO2020]粉刷墙壁

VP的时候打满了63分,没想到正解竟是两个暴力的合体/dk

首先,在这里定义粉刷匠的下家关系:
对于粉刷匠 i , j i,j i,j,如果 i + 1 = j i+1=j i+1=j 或者 i = m , j = 1 i=m,j=1 i=m,j=1,则称 j j j i i i 的下家

6分: f ( k ) ≤ 1 f(k)\le 1 f(k)1

可以发现,对于每一次操作,要么所有的粉刷匠都刷一格,要么都不刷。所以,我们尝试对于每一格,是否存在一种粉刷顺序使得可以刷完后面的 m m m 格。

f i f_i fi 表示从第 i i i 格开始刷最多可以往后刷多远(包括第 i i i 格)
转移方程: f i = f i + 1 + 1 f_i=f_{i+1}+1 fi=fi+1+1
如果 i + 1 i+1 i+1 不是 i i i 的下家,则 f i = 1 f_i=1 fi=1

时间复杂度 O ( n ) \mathcal O(n) O(n)

51分

上一档部分分考虑的是每个格子只能由一个粉刷匠刷,现在我们考虑把问题扩展到多个粉刷匠

f i , j f_{i,j} fi,j 表示位置 i i i 由编号为 j j j 的粉刷匠来刷,最多可以往后刷多少格(包括第 i i i 格)

转移方程: f i , j = f i + 1 , k f_{i,j}=f_{i+1,k} fi,j=fi+1,k,这里 k k k j j j 的下家
如果粉刷匠 j j j 不能刷第 i i i 格,则 f i , j = 0 f_{i,j}=0 fi,j=0

时空复杂度 O ( n m ) \mathcal O(nm) O(nm)

100分

观察上面51分的做法,可以发现他进行了许多冗余的操作:
对于位置 i i i,所有不能刷第 i i i 格的粉刷匠都被访问了一遍。

那么,我们可以预处理出每个位置可能被哪些粉刷匠刷,并且在转移时只转移可以刷第 i i i 格的粉刷匠
这样,时间复杂度就变成了 O ( ∑ f ( k ) ) \mathcal O(\sum f(k)) O(f(k))
注意这里的空间也也需要优化,可以使用滚动数组或者用 map 优化掉后一维,代码中采用的是后者

#include <vector>
#include<map>
using namespace std;
const int Maxn=1e5+10;
map <int,int> f[Maxn];
vector <int> c[Maxn],b[Maxn];
int a[Maxn];
bool flag[Maxn];
int n,m,k;
int minimumInstructions(
    int N, int M, int K, std::vector<int> C,
    std::vector<int> A, std::vector<std::vector<int>> B)
{
	n=N,m=M,k=K;
	for(int i=1;i<=n;++i)
	a[i]=C[i-1]+1;
	for(int i=1;i<=m;++i)
	{
		int tmp=A[i-1];
		for(int j=0;j<tmp;++j)
		b[B[i-1][j]+1].push_back(i);
	}
	for(int i=1;i<=n;++i)
	c[i]=b[a[i]];
	for(int i=n;i;--i)
	for(int j=0;j<c[i].size();++j)
	{
		int x=c[i][j];
		int y=c[i][j]+1;
		if(y>m)y=1;
		f[i][x]=1;
		if(i+1<=n)f[i][x]=f[i+1][y]+1;
		f[i][x]=min(f[i][x],m);
		if(f[i][x]==m)flag[i]=1;
	}
	int ret=0;
	for(int i=n-m+1;i;--i)
	{
		if(!flag[i])return -1;
		++ret;
		if(i==1)break;
		int tmp=max(i-m,1);
		for(int j=tmp;j<i;++j)
		if(flag[j] || j==i-1){i=j+1;break;}
	}
	return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值