P6764 [APIO2020] 粉刷墙壁

思路:

本质上能进行的操作就是我们算出从第 \(i\) 块砖开始,连续刷 \(M\) 块砖,是否有承包商可以刷出期望颜色。

那么设 \(f_i\) 表示 \(i,i+m-1\) 是否合法,那么就变成了最小区间覆盖问题。
最小区间覆盖问题

令 \(Max\) 表示当前覆盖了 \(1,Max\)。

那么我们需要找到左端点在 \(1,Max+1\) 内,且右端点最大的区间。

在本题中因为区间长度都为 \(m\),那么我们只需要找到尽可能在最后的合法 \(f_i\)。

此时 \(Max \gets i+m-1\),那么对于小于 \(j\) 的 \(i\),是无法覆盖到 \(i+m-1\) 之后的,于是就没有贡献了,于是我们可以直接走指针维护。
最小区间覆盖代码

cpp 复制代码
ll get(){
	ll Max=-1,x=0,id,ans=0;
	while(Max<n-1){
		id=-1;
		while(x<=Max+1&&x<n){
			if(f[x])
			  id=x;
			x++;
		}
		if(id==-1)
		  return -1;
		Max=id+m-1;
		ans++;
	}
	return ans;
}

那么我们需要考虑的就是如何求出 \(f_i\)。

28pts:

对于每个 \(i\),暴力枚举一个 \(j\),然后查看是否能匹配上。

时间复杂度为 \(O(NM^2)\)。

51pts:

考虑动态规划优化,定义 \(dp_{i,j}\) 表示从第 \(i\) 块墙壁开始,从第 \(j\) 个商家开刷最多能刷几块墙。

那么若第 \(j\) 个商家不能刷第 \(i\) 个墙壁,则:

\dp_{i,j}=0 \\

否则能刷:

\dp_{i,j} = dp_{i+1,(j+1) \\bmod M} + 1 \\

注意,空间开不下,考虑滚动数组优化。

时间复杂度为 \(O(NM)\)。

震惊的是,这玩意儿竟然过了......

离大谱。
O(NM) 代码

cpp 复制代码
int minimumInstructions(int N, int M, int K,vector<int> C,vector<int> A,vector<vector<int>> B){
	n=N,m=M,k=K;
	for(int i=0;i<n;i++)
	  a[i]=C[i];
	for(int i=0;i<m;i++){
		len[i]=A[i];
		for(auto v:B[i])
		  s[v].push_back(i);
	}
	for(int i=n-1;i>=0;i--){
		for(int j=0;j<m;j++)
		  dp[i&1ll][j]=0;
		for(auto j:s[a[i]]){
			dp[i&1ll][j]=dp[(i&1ll)^1ll][(j+1)%m]+1;
			if(dp[i&1ll][j]>=m)
			  f[i]=1;
		}
	}
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB"<<'\n';
	return get();
}

100pts:

注意到可以优化上面状态转移的 \(j\)。

我们可以提前预处理能刷颜色 \(x\) 的承包商的集合 \(S_x\),那么 \(j \in S_{c_i}\)。

但是因为是滚动数组,实时清空的话复杂度又上去了,那么再维护一个时间戳即可。

时间复杂度为 \(O(\sum f(k))\)。

完整代码:

cpp 复制代码
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef int ll;
bool Begin;
const ll N=100100,M=50050;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll n,m,k;
ll a[N],len[M];
ll dp[2][M],low[2][M];
vector<ll> s[N];
bool f[N];
bool End;
ll get(){
	ll Max=-1,x=0,id,ans=0;
	while(Max<n-1){
		id=-1;
		while(x<=Max+1&&x<n){
			if(f[x])
			  id=x;
			x++;
		}
		if(id==-1)
		  return -1;
		Max=id+m-1;
		ans++;
	}
	return ans;
}
int minimumInstructions(int N, int M, int K,vector<int> C,vector<int> A,vector<vector<int>> B){
	n=N,m=M,k=K;
	for(int i=0;i<n;i++)
	  a[i]=C[i];
	for(int i=0;i<m;i++){
		len[i]=A[i];
		for(auto v:B[i])
		  s[v].push_back(i);
	}
	for(int i=n-1;i>=0;i--){
		for(auto j:s[a[i]]){
			if(low[(i&1ll)^1ll][(j+1)%m]!=i+1)
			  dp[i&1ll][j]=1;
			else
			  dp[i&1ll][j]=dp[(i&1ll)^1ll][(j+1)%m]+1;
			low[i&1ll][j]=i;
			if(dp[i&1ll][j]>=m)
			  f[i]=1;
		}
	}
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB"<<'\n';
	return get();
}
相关推荐
彧azz6 天前
算法设计与分析:贪心与动态规划
数据结构·学习·算法·贪心算法·动态规划
伟大的车尔尼8 天前
贪心题目:优势洗牌
贪心
Logic1018 天前
C语言/数据结构动态规划题解:Kadane算法求最大子数组和——O(n)时间O(1)空间
c语言·数据结构·动态规划·贪心·时间复杂度·算法题·最大子数组和
梦帮科技8 天前
AI 音乐产品的发布工程:验证门、数据发布、回滚与生产运维纪律
数据结构·数据库·架构·node.js·音视频·动态规划·推荐算法
语戚9 天前
力扣 1621. 大小为K的不重叠线段的数目:动态规划(Java 实现)
java·算法·leetcode·动态规划·力扣·dp
青山木9 天前
Hot 100 --- 最长有效括号
java·数据结构·算法·leetcode·动态规划
这料鬼有毒9 天前
二刷hot100-1143.最长公共子序列
算法·leetcode·动态规划
Tisfy9 天前
LeetCode 1477.找两个和为目标值且不重叠的子数组:滑动窗口 + 记录前值(动态规划)
动态规划·题解·dp·滑动窗口
ynchyong10 天前
动态规划解题思路:入门篇
动态规划
伟大的车尔尼10 天前
贪心题目:分发饼干
贪心