231028校内赛
T1 放学路
一眼发现很像经典 \(dp\) 题,方格取数
但是不同点在于这道 \(dp\) 不同之处在于两人起点不同且复杂度需要至少 \(\mathcal O(n^2)\)
我们首先容易想出来一个假的 \(\mathcal O(n^2)\) 的做法是枚举一个交叉点,预处理算四个端点过去的距离
但是明显错误有两处
- 交叉的不一定是一个点
- 四个端点过去的路径可能会重复
那么我们可以考虑将点换成一条链(包含一个点的情况)来进行枚举
这时的复杂度会退化为 \(\mathcal O(n^3)\) ,但是我们可以保证这个 \(dp\) 的正确性
只要分别对链的两个端点讨论进入和出去的情况就可以了
最后需要将这个 \(dp\) 优化一下,枚举每一个点进行转移,后面再统计答案
我才不会说我忘了最后的优化呢
#include<bits/stdc++.h>
#define N 1010
#define inf (0x3f3f3f3f3f3f3f3fll)
#define int long long
using namespace std;
int n,ans,a[N][N],dp[2][N][N],f[5][N][N],g[2][N][N],suf[2][N][N],suml[N][N],sumr[N][N];
signed main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
memset(f,-0x3f,sizeof(f));
memset(suf,-0x3f,sizeof(suf));
cin>>n;ans = -inf;
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++){
cin>>a[i][j];
suml[i][j] = suml[i-1][j]+a[i][j];
sumr[i][j] = sumr[i][j-1]+a[i][j];
}
f[1][0][1] = f[1][1][0] = f[2][0][n] = f[2][1][n+1] =
f[3][n+1][n] = f[3][n][n+1] = f[4][n+1][1] = f[4][n][0] = 0;
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
f[1][i][j] = max(f[1][i-1][j],f[1][i][j-1])+a[i][j];
for(int i = 1;i<=n;i++)
for(int j = n;j>=1;j--)
f[2][i][j] = max(f[2][i-1][j],f[2][i][j+1])+a[i][j];
for(int i = n;i>=1;i--)
for(int j = n;j>=1;j--)
f[3][i][j] = max(f[3][i+1][j],f[3][i][j+1])+a[i][j];
for(int i = n;i>=1;i--)
for(int j = 1;j<=n;j++)
f[4][i][j] = max(f[4][i+1][j],f[4][i][j-1])+a[i][j];
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
ans = max(ans,max(f[1][i-1][j]+f[3][i+1][j]+f[4][i][j-1]+f[2][i][j+1],
f[1][i][j-1]+f[3][i][j+1]+f[2][i-1][j]+f[4][i+1][j])+a[i][j]);
dp[0][i][j] = max(f[1][i][j-1]+max(f[2][i-1][j],f[2][i][j+1]),
f[2][i][j+1]+max(f[1][i-1][j],f[1][i][j-1]));
dp[1][i][j] = max(f[1][i-1][j]+max(f[4][i+1][j],f[4][i][j-1]),
f[4][i+1][j]+max(f[1][i-1][j],f[1][i][j-1]));
g[0][i][j] = max(f[3][i][j+1]+max(f[4][i][j-1],f[4][i+1][j]),
f[4][i][j-1]+max(f[3][i][j+1],f[3][i+1][j]));
g[1][i][j] = max(f[2][i-1][j]+max(f[3][i+1][j],f[3][i][j+1]),
f[3][i+1][j]+max(f[2][i-1][j],f[2][i][j+1]));
dp[0][i][j]-=suml[i-1][j];g[0][i][j]+=suml[i][j];
dp[1][i][j]-=sumr[i][j-1];g[1][i][j]+=sumr[i][j];
}
}
for(int i = n;i>=1;i--)
for(int j = n;j>=1;j--){
suf[0][i][j] = max(suf[0][i+1][j],g[0][i][j]);
suf[1][i][j] = max(suf[1][i][j+1],g[1][i][j]);
}
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
ans = max({ans,dp[0][i][j]+suf[0][i+1][j],dp[1][i][j]+suf[1][i][j+1]});
cout<<ans;
return 0;
}
T2 排列
有一个很简单的方法
把每一个区间的最小值求出来,然后对这个三角形作数字三角形一样的 \(dp\)
注意的一点是在前面的两个点相等时,需要把重复的减掉,也就是前面的左边那个点的上面那个点
换成坐标就是 \(a_{i-1,j}==a_{i-1,j-1}\) 时,\(ans-=f_{i-2,j-1}\)
#include<bits/stdc++.h>
#define N 5010
#define mod 998244353
using namespace std;
int n,ans,f[N][N],dp[N][N];
signed main(){
freopen("per.in","r",stdin);
freopen("per.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i = 1;i<=n;i++)
cin>>f[n][i];
for(int i = n-1;i>=1;i--)
for(int j = 1;j<=i;j++)
f[i][j] = min(f[i+1][j],f[i+1][j+1]);
dp[1][1] = 1;
for(int i = 2;i<=n;i++)
for(int j = 1;j<=n;j++){
dp[i][j] = (dp[i-1][j]+dp[i-1][j-1])%mod;
if(f[i-1][j-1]==f[i-1][j])
dp[i][j] = (dp[i][j]-dp[i-2][j-1]+mod)%mod;
}
for(int i = 1;i<=n;i++)
ans = (ans+dp[n][i])%mod;
cout<<ans;
return 0;
}
T3 选拔
很有趣的一道思维题,但是我不是很懂
发现这个问题相当于每次选一个人,把区间跟它有交的全部扔掉,然后选最少个人使得能把不包含 \(i\) 的所有区间全扔掉
每次选一个右端点最小的区间选中它的右端点之后扔区间,不断执行这个过程
或者反过来每次选一个左端点最大的区间选中它的左端点之后扔区间,不断执行这个过程
两种做法刚好能对每个前缀和每个后缀求解
建议使用排序后线性的做法通过,否则可能会被卡常
#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 int ll
#define N 510005
using namespace std;
int n,m;
int s[N];
pa all[N];
int pre[N],suf[N];
int L[N],R[N];
poly G[N],E[N];
signed main()
{
freopen("select.in","r",stdin);
freopen("select.out","w",stdout);
IOS;
cin.tie(0);
cin>>n>>m;
for (int i=0;i<m;i++)
cin>>L[i]>>R[i];
int ans=n+2;
poly g;
bool bl=1;
int x=m;
for (int i=0;i<m;i++)
{
all[i]=mp(L[i],R[i]);
}
sort(all,all+m,[&](pa x,pa y)
{
return x.se<y.se;
});
for (int i=0;i<m;i++)L[i]=all[i].fi,R[i]=all[i].se;
// for (int i=0;i<n;i++)
{
int l=-1;
int nows=0;
for (int j=0;j<m;j++)
{
if (L[j]>l)
{
l=R[j];
nows++;
}
pre[R[j]]=nows;
}
for (int i=1;i<n;i++) pre[i]=max(pre[i],pre[i-1]);
}
sort(all,all+m,[&](pa x,pa y)
{
return x.fi>y.fi;
});
for (int i=0;i<m;i++)L[i]=all[i].fi,R[i]=all[i].se;
{
int l=n+1;
int nows=0;
for (int j=0;j<m;j++)
{
if (R[j]<l)
{
l=L[j];
nows++;
}
suf[L[j]]=nows;
}
for (int i=n-2;i>=0;i--) suf[i]=max(suf[i],suf[i+1]);
}
for (int i=0;i<n;i++)
{
s[i]=0;
if (i>0) s[i]+=pre[i-1];
if (i+1<n) s[i]+=suf[i+1];
ans=min(ans,s[i]);
}
cout<<ans+1<<'\n';
for (int i=0;i<n;i++)if (s[i]==ans) cout<<i<<" ";
}
梦与现实间挣扎着,所求为何
你可以借走我的文章,但你借不走我的智慧 虽然我是傻逼本文来自博客园,作者:cztq,转载请注明原文链接:https://www.cnblogs.com/cztq/p/17798656.html