IOI 2023 Day1 Soccer 题解
这个题很有趣啊,感觉自己想出来这个题的过程非常有趣。
让我们先来编一个合法条件,一种简洁的刻画方式是:若干个长度区间不断扩张宽度区间不断缩短的矩阵的并。
因此尝试利用这个结构来进行 dp,一个直观的想法是我们枚举最长的那个矩形覆盖了哪一列,然后在这一列上对行区间进行 dp。具体是
设
这个做法是
实际上上面这个做法有很多可以优化的地方。首先我们考虑如何抛开枚举哪一列这个步骤来刻画状态中的所谓“空隙”。直接用行区间
也就是说,我们可以直接用矩阵上的一个位置来表示一个空隙。我们将空隙而接下来转移也很简单,跟上面一样,每次要么
对于每个空隙求列区间
#include "soccer.h"
#include<bits/stdc++.h>
#define poly vector<int>
#define IOS ios::sync_with_stdio(false)
#define ll long long
#define mp make_pair
#define mt make_tuple
#define pa pair < int,int >
#define fi first
#define se second
#define inf 1e18
#define mod 998244353
// #define int ll
#define N 2005
using namespace std;
namespace
{
int dp[N][N];
int pos[N][N];
int L[N][N],R[N][N];
}
int biggest_stadium(int n, std::vector<std::vector<int>> aa)
{
int ans=0;
vector<vector<pa>>tong(n+1,vector<pa>());
for (int j=1;j<=n;j++)
{
for (int i=1;i<=n;i++)
{
dp[i][j]=0;
if(aa[i-1][j-1]) pos[i][j]=i;
else pos[i][j]=pos[i-1][j];
tong[i-pos[i][j]].push_back(mp(i,j));
}
}
for (int i=1;i<=n;i++)
{
L[i][0]=0;
for (int j=1;j<=n;j++)
if (aa[i-1][j-2]) L[i][j]=j-1;else L[i][j]=L[i][j-1];
R[i][n+1]=n+1;
for (int j=n;j>=1;j--)
if (aa[i-1][j]) R[i][j]=j+1;else R[i][j]=R[i][j+1];
}
for (int d=1;d<=n;d++)
{
for (auto [i,j]:tong[d])
{
if (pos[i][j]!=i-1)
{
R[i][j]=min(R[i][j],R[i-1][j]);
L[i][j]=max(L[i][j],L[i-1][j]);
}
dp[i][j]=dp[i-1][j]+R[i][j]-L[i][j]-1;
if (L[i][j]>0)
{
int len=pos[i][L[i][j]]-pos[i][j];
dp[i][j]=max(dp[i][j],dp[i][L[i][j]]+len*(R[i][j]-L[i][j]-1));
}
if (R[i][j]<=n)
{
int len=pos[i][R[i][j]]-pos[i][j];
dp[i][j]=max(dp[i][j],dp[i][R[i][j]]+len*(R[i][j]-L[i][j]-1));
}
ans=max(ans,dp[i][j]);
}
}
return ans;
}