洛谷 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;
}