题目
n*m(n,m<=1e3)的矩阵,由*和.组成
从中挑出不超过n*m个star(star被定义为全为*的十字架,上下左右伸出的部分需要等长)
*可以被多个star公用,
如果所有*都能被star覆盖,输出挑选方案
否则输出-1
思路来源
涛神
题解
先预处理左条、右条、上条、下条的最大长度,分四个方向dp
这里记录的是不包括中心点的长度,所以需要相邻两个都为*
十字架半径,显然为四者取最小
代码1:自己瞎搞的,覆盖的时候,up down left right记录上次覆盖到哪里了,避免重复覆盖
这样分四个方向覆盖就好了,最多遍历8次全图,复杂度O(n*m),注意dp增序降序
代码2:涛神讲的,差分,记rr为半径,考虑[i-rr,i+rr][j-rr,j+rr]的区域
就等价成分别在横条和竖条上区间+1,维护差分数组,端点+1-1,
check时分别对横条和竖条求前缀和,判断是否存在行列都等于0的*,这样的*是没被覆盖的
心得
差分常常和线段有关,如区间染色、区间加等
还是要多多练习,培养思维
代码1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1e3+5;
int n,m,cnt;
int r[4][N][N],R[N][N];
int up[N],down[N],LEFT[N],RIGHT[N];
char s[N][N];
bool ok[N][N],vis[N][N],flag;//2 star 1 * 0 .
struct node
{
int x,y,r;
}e[N*N];
int RR(int a,int b,int c,int d)
{
return min(min(a,b),min(c,d));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(s[i][j]!='*')continue;
if(s[i][j-1]=='*')r[0][i][j]=r[0][i][j-1]+1;//左
}
for(int i=1;i<=n;++i)
for(int j=m;j>=1;--j)
{
if(s[i][j]!='*')continue;
if(s[i][j+1]=='*')r[1][i][j]=r[1][i][j+1]+1;//右
}
for(int j=1;j<=m;++j)
for(int i=1;i<=n;++i)
{
if(s[i][j]!='*')continue;
if(s[i-1][j]=='*')r[2][i][j]=r[2][i-1][j]+1;//上
}
for(int j=1;j<=m;++j)
for(int i=n;i>=1;--i)
{
if(s[i][j]!='*')continue;
if(s[i+1][j]=='*')r[3][i][j]=r[3][i+1][j]+1;//下
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int rr=RR(r[0][i][j],r[1][i][j],r[2][i][j],r[3][i][j]);
if(rr==0)continue;
e[++cnt]=node{i,j,rr};
ok[i][j]=1;
R[i][j]=rr;
}
}
memset(LEFT,0x3f,sizeof LEFT);
for(int i=1;i<=n;++i)
{
for(int j=m;j>=1;--j)
{
if(!ok[i][j])continue;
vis[i][j]=1;
LEFT[i]=min(LEFT[i],j);
for(int y=LEFT[i]-1;y>=j-R[i][j];y--)
vis[i][y]=1;
LEFT[i]=min(LEFT[i],j-R[i][j]);
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(!ok[i][j])continue;
vis[i][j]=1;
RIGHT[i]=max(RIGHT[i],j);
for(int y=RIGHT[i]+1;y<=j+R[i][j];y++)
vis[i][y]=1;
RIGHT[i]=max(RIGHT[i],j+R[i][j]);
}
}
memset(up,0x3f,sizeof up);
for(int j=1;j<=m;++j)
{
for(int i=n;i>=1;--i)
{
if(!ok[i][j])continue;
vis[i][j]=1;
up[j]=min(up[j],i);
for(int x=up[j]-1;x>=i-R[i][j];x--)
vis[x][j]=1;
up[j]=min(up[j],i-R[i][j]);
}
}
for(int j=1;j<=m;++j)
{
for(int i=1;i<=n;++i)
{
if(!ok[i][j])continue;
vis[i][j]=1;
down[j]=max(down[j],i);
for(int x=down[j]+1;x<=i+R[i][j];x++)
vis[x][j]=1;
down[j]=max(down[j],i+R[i][j]);
}
}
for(int i=1;i<=n&&!flag;++i)
{
for(int j=1;j<=m&&!flag;++j)
{
if(s[i][j]!='*')continue;
if(!vis[i][j])
{
//printf("(%d,%d)",i,j);
flag=1;
}
}
}
if(flag)puts("-1");
else
{
printf("%d\n",cnt);
for(int i=1;i<=cnt;++i)
printf("%d %d %d\n",e[i].x,e[i].y,e[i].r);
}
return 0;
}
代码2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1e3+5;
int n,m,cnt;
int r[4][N][N];
int row[N][N],col[N][N];
char s[N][N];
bool ok;
struct node
{
int x,y,r;
}e[N*N];
int RR(int a,int b,int c,int d)
{
return min(min(a,b),min(c,d));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(s[i][j]!='*')continue;
if(s[i][j-1]=='*')r[0][i][j]=r[0][i][j-1]+1;//左
}
for(int i=1;i<=n;++i)
for(int j=m;j>=1;--j)
{
if(s[i][j]!='*')continue;
if(s[i][j+1]=='*')r[1][i][j]=r[1][i][j+1]+1;//右
}
for(int j=1;j<=m;++j)
for(int i=1;i<=n;++i)
{
if(s[i][j]!='*')continue;
if(s[i-1][j]=='*')r[2][i][j]=r[2][i-1][j]+1;//上
}
for(int j=1;j<=m;++j)
for(int i=n;i>=1;--i)
{
if(s[i][j]!='*')continue;
if(s[i+1][j]=='*')r[3][i][j]=r[3][i+1][j]+1;//下
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
int rr=RR(r[0][i][j],r[1][i][j],r[2][i][j],r[3][i][j]);
if(rr==0)continue;
e[++cnt]=node{i,j,rr};
row[i][j-rr]++;row[i][j+rr+1]--;//差分 []区间染色
col[i-rr][j]++;col[i+rr+1][j]--;
}
}
for(int i=1;i<=n&&!ok;++i)
{
for(int j=1;j<=m&&!ok;++j)
{
row[i][j]+=row[i][j-1];
col[i][j]+=col[i-1][j];
//printf("(%d,%d) ",row[i][j],col[i][j]);
if(s[i][j]!='*')continue;
if(row[i][j]<=0&&col[i][j]<=0)ok=1;
}
//puts("");
}
if(ok)puts("-1");
else
{
printf("%d\n",cnt);
for(int i=1;i<=cnt;++i)
printf("%d %d %d\n",e[i].x,e[i].y,e[i].r);
}
return 0;
}