洛谷 P6764 [APIO2020]粉刷墙壁(dp,贪心)

传送门


解题思路

第一步,求出从每个墙壁开始刷是否合法。
第二步,合法的墙壁位置作为每个区间的左端点,变成区间覆盖问题。
第二步很显然是个贪心,重点是第一步如何解决。
我们设dp[i][j]表示从第i个墙壁第j个承包商开始刷能刷的墙壁的数量。
那么

  • 当第j个承包商能刷第i个墙的时候,dp[i][j]=dp[i+1][j+1]+1;
  • 当第j个承包商不能刷第i个墙的时候,dp[i][j]=0;

优化:
空间上:滚动数组。
时间上:对于确定的i,枚举喜欢c[i]颜色的承包商。
交互题真的恶心,不知道怎么调试代码

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=100005;
vector<int> d[maxn];
int n,m,k,vis[2][maxn],dp[2][maxn],ok[maxn];
struct node{
	int l,r;
}s[maxn];
bool cmp(node a,node b){
	return a.l<b.l;
}
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=1;i<=n;i++) C[i-1]++;
	for(int i=1;i<=m;i++){
		for(int j=0;j<A[i-1];j++) B[i-1][j]++;
	}
	for(int i=1;i<maxn;i++) d[i].push_back(0);
	for(int i=1;i<=m;i++){
		for(int j=0;j<A[i-1];j++) d[B[i-1][j]][0]++,d[B[i-1][j]].push_back(i);
	}
	for(int i=n;i>=1;i--){
		for(int j=1;j<=d[C[i-1]][0];j++){
			if(vis[(i+1)&1][d[C[i-1]][j]%m+1]==i+1) dp[i&1][d[C[i-1]][j]]=dp[(i+1)&1][d[C[i-1]][j]%m+1]+1;
			else dp[i&1][d[C[i-1]][j]]=1;
			vis[i&1][d[C[i-1]][j]]=i; 
			if(dp[i&1][d[C[i-1]][j]]>=m) ok[i]=1;
		}
	}
	int cnt=0;
	for(int i=1;i<=n-m+1;i++){
		if(ok[i]){
			cnt++;
			s[cnt].l=i;
			s[cnt].r=i+m-1;
		}
	}
	sort(s+1,s+cnt+1,cmp);
	int now=0,ans=0;
	for(int i=1;i<=cnt;i++){
		if(s[i].l>now+1){
			return -1;
		}
		int maxr=s[i].r;
		while(i<cnt&&s[i+1].l<=now+1){
			maxr=max(maxr,s[i+1].r);
			i++;
		}
		ans++;
		now=maxr;
	}
	if(now<n){
		return -1;
	}
	return ans;
}
posted @ 2021-09-12 17:54  尹昱钦  阅读(180)  评论(0编辑  收藏  举报