a题
翻译:
您正在给一个无限方形网格着色,网格中的所有单元格最初都是白色的。为此,我们会给你 n 个图章。每个图章都是宽
、高
的矩形。
每个图章只能使用一次,在网格上给与图章大小相同的矩形涂上黑色。您不能旋转印章,对于每个单元格,印章必须完全覆盖或完全不覆盖。您可以在网格上的任何位置使用图章,即使图章区域覆盖的部分或全部单元格已经是黑色的。
使用所有图章后,黑色方格相连区域的最小周长之和是多少?
思路:
要想周长小每个图章的重叠面积就得尽量大,而周长由图章的最长高和最长底决定,即周长=2*(最长高+最长底)。
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll,ll>;
const int N = 4e5+100;
ll t,n,w,h;
void solve(){
cin>>n;
ll ww = 0,hh = 0;
for (int i=0;i<n;i++){
cin>>w>>h;
ww = max(ww,w);
hh = max(hh,h);
}
cout<<2*(ww+hh)<<endl;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>t;
while (t--) solve();
}
b题
翻译:
斯大林排序(Stalin Sort)是一种幽默的排序算法,旨在删除不合适的元素,而不是费心地对它们进行正确排序,其时间复杂度为 O(n)。
算法如下:从数组中的第二个元素开始,如果它严格小于前一个元素(忽略那些已经被删除的元素),那么就删除它。继续迭代数组,直到按非递减顺序排序。例如,数组[1,4,2,3,6,5,5,7,7]在斯大林排序后会变成[1,4,6,7,7]。
如果对数组中的任意子数组重复执行斯大林排序,并能按非递增顺序排序,那么我们就将该数组定义为脆弱数组。
给定一个由 n 个整数组成的数组 a,求从数组中删除的整数的最小个数,以使数组变得脆弱。
如果数组 a 可以从数组 b 中删除开头的几个(可能是零或全部)元素和结尾的几个(可能是零或全部)元素,那么数组 a 就是数组 b 的子数组。
思路:
照他这个删法要变成非递增顺序排序,只有变成一条直线(即所有剩余值相同),要实现只要保持左端点的右侧没有比该点大的数即可。
遍历每个点i当左端点用,比较花费大小,花费=i-1+右侧比i大的数。
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll,ll>;
const int N = 4e5+100;
ll t,n;
void solve(){
cin>>n;
vector<ll> a(n+10);
for (int i=0;i<n;i++) cin>>a[i];
ll ans = LLONG_MAX;
for (int i=0;i<n;i++){
ll temp = i;
for (int j=i+1;j<n;j++){
temp += a[i]<a[j];
}
ans = min(temp,ans);
}
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>t;
while (t--) solve();
}
c题
翻译:
给你一个最初包含 n 个整数的数组 a。在一次操作中,你必须完成以下操作:
- 选择一个位置 i,使得
和
其中
是数组的当前大小。
- 在 a 的末尾添加 i-1 个零。
尽可能多次地执行此操作后,数组 a 的最大可能长度是多少?
思路:
也就是一个数加上其下标可以实现数组的增长,而添加0必定不能使数组再增长。递归所有f(new_len)=f(len)+a[len](a为字典键值对为len和所有和为len的i)
实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll,ll>;
ll t,n,ans;
map<ll,vector<ll>> a;
set<ll> vis;
void dfs(ll len){
if (vis.find(len)!=vis.end()) return;
vis.insert(len);
ans = max(ans,len);
for (auto i=a[len].begin();i!=a[len].end();i++){
dfs(len+(*i));
}
}
void solve(){
cin>>n;
ans = n;a.clear();vis.clear();
for (ll i=0,num;i<n;i++) cin>>num,a[i+num].push_back(i);
dfs(n);
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>t;
while (t--) solve();
}
D1题
翻译:
给你一个长度为 n 的数组 a 和一个长度为 m 的数组 b(
对于
) 。最初,k 的值是 1。你的目标是通过重复执行这两种操作中的一种,使数组 a 为空:
- 类型1 -- 如果 k 的值小于m,且数组 a 不为空,则可以将 k 的值增加 1。这不会产生任何费用。
- 类型2 -- 删除数组 a 的一个非空前缀,使其总和不超过
。这会产生 m-k 的代价。
你需要最小化操作的花费,使数组 a 为空。如果不可能通过任何操作序列做到这一点,则输出-1。否则,输出操作的最小总花费。
思路:
他分两个操作 1.a没删光时k+1;2.移除a的前缀和不大于
的部分。
定义f[i][j]为目前k为j时,a数组删到i处所需要的花费。
操作1:f[i][j+1]=f[i][j]
操作2:f[(当前位置使用
能到的最远处)][j] = f[i][j]+(m-k)
为什么不管i与最远处之间的位置?
因为要让结果变好,每次删必定删越多越好,关心之间的没有意义
如何快速得出当前位置使用
能到的最远处?
使用前缀和+二分搜索,找到从i开始前缀和小于等于
的位置。
或使用滑动窗口,由于k不变i可一直向后推。
下面的dp还可以优化为一维可以试试(微笑)。
实现:
二分实现:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll,ll>;
const ll N = 3e5+100;
const ll MAXN = 1e17;
ll t,n,m;
vector<ll> a(N),b(N);
void solve(){
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i],a[i]+=a[i-1];
for (int i=1;i<=m;i++) cin>>b[i];
vector<vector<ll>> dp(n+1,vector<ll>(m+1,MAXN));
for (int i=0;i<=m;i++) dp[0][i] = 0;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
dp[i][j] = min(dp[i][j],dp[i][j-1]);
int r = upper_bound(a.begin()+1,a.begin()+n+1,a[i-1]+b[j])-a.begin()-1;
dp[r][j] = min(dp[r][j],dp[i-1][j]+m-j);
}
}
if (dp[n][m]==MAXN) dp[n][m]=-1;
cout<<dp[n][m]<<endl;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>t;
while (t--) solve();
}
滑动窗口:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll,ll>;
const ll N = 3e5+100;
const ll MAXN = 1e17;
ll t,n,m;
vector<ll> a(N),b(N);
void solve(){
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i],a[i]+=a[i-1];
for (int i=1;i<=m;i++) cin>>b[i];
vector<vector<ll>> dp(n+1,vector<ll>(m+1,MAXN));
for (int i=0;i<=m;i++) dp[0][i] = 0;
for (int i=1;i<=m;i++){
for (int j=1,l=0;j<=n;j++){
while (a[j]-a[l]>b[i]) l++;
dp[j][i] = min(dp[j][i],dp[j][i-1]);
dp[j][i] = min(dp[j][i],dp[l][i]+m-i);
}
}
if (dp[n][m]==MAXN) dp[n][m]=-1;
cout<<dp[n][m]<<endl;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>t;
while (t--) solve();
}