对dp还不是特别熟练
只做到了C(还是太菜了),开始前刚好各种事情来了,vp晚了10多分钟开才始做题,喜提排名(不是)3000+,后面有时间就尽量把dp补掉
A. Rectangle Arrangement
你需要在一个无限的方格网格上涂色,所有格子最初都是白色的。为了完成这项任务,你有 \(n\) 个印章。每个印章是一个宽度为 \(w_i\)、高度为 \(h_i\) 的矩形。
你将使用 每个 印章 恰好一次 在网格上涂一个与印章同样大小的矩形区域为黑色。你不能旋转印章,对于每个格子,印章必须完全覆盖它或者完全不覆盖它。你可以在网格上的任何位置使用印章,即使印章覆盖的一些或全部格子已经是黑色的。
在使用完所有印章之后,你能得到的黑色方格区域的 周长之和 的最小值是多少?
\(Input\)
每个测试包含多个测试案例。第一行包含测试案例的数量 t(1≤t≤500)。每个测试案例的描述紧随其后。
每个测试案例的第一行包含一个整数 n(1≤n≤100)。
接下来的 n 行中,第 i 行包含两个整数 wi 和 hi(1≤wi,hi≤100),分别代表第 i 个印章的宽度和高度。
\(Output\)
对于每个测试案例,输出一个整数——在使用完所有印章后,你能得到的黑色方格区域周长之和的最小值。
\(Sample\)
5
5
1 5
2 4
3 3
4 2
5 1
3
2 2
1 1
1 2
1
3 2
3
100 100
100 100
100 100
4
1 4
2 3
1 5
3 2
20
8
10
400
16
思路:这题我是老老实实进行了区域覆盖,然后dfs了一遍区域,看周长
没想到是小学数学,\(2*max(h)*max(w)\),也确实是啊,vp时真是脑子抽了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
//#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
struct s
{ll x,y;
}p[2500];
ll a[400][400];
ll d[4]={0,1,-1,0};
ll e[4]={1,0,0,-1};
int main()
{fio();ll t;cin>>t;while(t--){for(ll i=1;i<=100;i++){for(ll j=1;j<=100;j++)a[i][j]=0;}ll n;cin>>n;for(ll i=1;i<=n;i++){ll w,h;cin>>w>>h;for(ll k=1;k<=w;k++){for(ll u=1;u<=h;u++){a[k][u]=1;}}}ll ans=0;for(ll i=0;i<=101;i++){for(ll j=0;j<=101;j++){for(ll u=0;u<=3;u++){ll nx=i+d[u];ll ny=j+e[u];if(nx>=1&&nx<=100&&ny>=1&&ny<=100&&a[i][j]==0){if(a[nx][ny])ans++;}} }}cout<<ans<<endl;}
}
B. Stalin Sort
斯大林排序(Stalin Sort)是一种幽默的排序算法,它不是为了正确地对元素进行排序,而是为了消除那些不在适当位置的元素,从而实现 \(\mathcal{O}(n)\) 的时间复杂度。
斯大林排序的步骤如下:从数组的第二个元素开始,如果它严格小于前一个元素(忽略那些已经被删除的元素),那么就删除它。继续遍历数组,直到数组按照非递减顺序排序。例如,数组 \([1, 4, 2, 3, 6, 5, 5, 7, 7]\) 在经过斯大林排序后变为 \([1, 4, 6, 7, 7]\)。
我们定义一个数组为“易受攻击”的,如果你可以通过反复对它的任意子数组应用斯大林排序,无论需要多少次,来将其排序成非递增顺序。
给定一个包含 \(n\) 个整数的数组 \(a\),确定必须从数组中移除的最小整数数量,以使其变得“易受攻击”。
\(^{\text{∗}}\) 如果数组 \(a\) 可以通过从数组 \(b\) 删除若干(可能为零或全部)开头和结尾的元素来获得,那么数组 \(a\) 就是数组 \(b\) 的一个子数组。
\(Input\)
每个测试包含多个测试案例。第一行包含一个整数 \(t\)(\(1 \le t \le 500\))——测试案例的数量。之后是测试案例的描述。
每个测试案例的第一行包含一个整数 \(n\)(\(1 \le n \le 2000\))——数组的大小。
每个测试案例的第二行包含 \(n\) 个整数 \(a_1, a_2, \ldots, a_n\)(\(1 \le a_i \le 10^9\))。
保证所有测试案例中 \(n\) 的总和不超过 \(2000\)。
\(Output\)
对于每个测试案例,输出一个整数——必须从数组中移除的最小整数数量,以使其变得“易受攻击”。
\(Sample\)
6
7
3 6 4 9 2 5 2
5
5 4 4 2 2
8
2 2 4 4 6 6 10 10
1
1000
9
6 8 9 10 12 9 7 5 4
7
300000000 600000000 400000000 900000000 200000000 400000000 200000000
2
0
6
0
4
2
样例解析:
在第一个测试案例中,最优解是移除数字 \(3\) 和 \(9\)。这样我们剩下的数组为 \(a = [6, 4, 2, 5, 2]\)。为了证明这个数组是“易受攻击”的,我们可以先对子数组 \([4, 2, 5]\) 应用斯大林排序,得到 \(a = [6, 4, 5, 2]\),然后对子数组 \([6, 4, 5]\) 应用斯大林排序,得到 \(a = [6, 2]\),这是一个非递增序列。
在第二个测试案例中,数组已经是非递增的,所以我们不需要移除任何整数。
思路:说了这么多,其实题意等价于问第一个是不是最大的数,枚举每一个数作为第一个,前面的要全删除,然后后面比这个数大的也要删除,这样子就可以保证排序到最后可以获得不递增的排列
不妨象想想,第一个数非最大数,且后面有更大数时是无解的
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
//#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[4500];
int main()
{fio();ll t;cin>>t;while(t--){ll n;cin>>n;for(ll i=1;i<=n;i++){cin>>a[i];}ll ans=9999999999;for(ll i=1;i<=n;i++){ll cnt=i-1;for(ll j=i+1;j<=n;j++){if(a[j]==a[i])continue;else if(a[j]>a[i])cnt++;}ans=min(ans,cnt);}cout<<ans<<endl;}
}
C. Add Zeros
您将获得一个最初包含 \(n\) 个整数的数组 \(a\)。在一个操作中,您必须执行以下操作:
选择一个位置 \(i\),使得 \(1 < i \leq |a|\) 且 \(a_i = |a| + 1 - i\),其中 \(|a|\) 是数组的当前大小。
在 \(a\) 的末尾追加 \(i - 1\) 个零。
在您想要多次执行此操作后,数组 \(a\) 的最大可能长度是多少?
\(Input\)
每个测试包含多个测试用例。第一行包含测试用例的数量 \(t\)(\(1 \le t \le 1000\))。测试用例的描述紧随其后。
每个测试用例的第一行包含 \(n\)(\(1 \le n \le 3 \cdot 10^5\))——数组 \(a\) 的长度。
每个测试用例的第二行包含 \(n\) 个整数 \(a_1, a_2, \ldots, a_n\)(\(1 \le a_i \le 10^{12}\))。
保证所有测试用例的 \(n\) 之和不超过 \(3 \cdot 10^5\)。
\(Output\)
对于每个测试用例,输出一个整数 —— 在执行一系列操作后,数组 \(a\) 的最大可能长度。
\(Sample\)
4
5
2 4 6 2 5
5
5 4 4 5 1
4
6 8 2 3
1
1
10
11
10
1
样例解析:
在第一个测试用例中,我们可以先选择 \(i = 4\),因为 \(a_4 = 5 + 1 - 4 = 2\)。在此之后,数组变为 \([2, 4, 6, 2, 5, 0, 0, 0]\)。然后我们可以选择 \(i = 3\),因为 \(a_3 = 8 + 1 - 3 = 6\)。在此之后,数组变为 \([2, 4, 6, 2, 5, 0, 0, 0, 0, 0]\),其长度为 10。可以证明,没有任何操作序列会使最终数组更长。
在第二个测试用例中,我们可以选择 \(i=2\),然后 \(i=3\),然后 \(ii=4\)。最终数组将是 \([5, 4, 4, 5, 1, 0, 0, 0, 0, 0, 0]\),长度为 11。
思路:其实由这个式子\(a_i = |a| + 1 - i\)可得到\(a_i - i = |a| + 1\),当一个数可以进行操作时,其必然符合这个式子,不妨先求出\(a_i - i -|a| - 1\)作为特征值然后反过来用特征值去记录符合的下标,然后每次增加i-1时,可以去看看是否有符合的特征值存在,如果存在,则进行搜索,不在就返回。因为答案显然是没有一个固定策略可选,所以选择记忆化递归(dp的一种形式)然后从以0作为特征值去搜即可算出答案,对于每个阶段的增值可能最好用map记住,然后从其他地方搜过来时,时间复杂度就大大减小了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
//#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll n;
map<ll,set<ll>>q;
map<ll,ll>op;
ll dfs(ll cnt,ll z)//记忆化?
{if(op[cnt]){return op[cnt]+z;}ll ans=z;for(auto j:q[cnt]){ans=max(ans,dfs(cnt+j-1,z+j-1));}op[cnt]=ans-z;return ans;
}
ll a[450000];
int main()
{fio();ll t;cin>>t;while(t--){op.clear();cin>>n;for(ll i=1;i<=n;i++){cin>>a[i];if(a[i]>=(n-i+1)&&i!=1){q[a[i]-(n-i+1)].insert(i);}}cout<<dfs(0,n)<<endl;q.clear();}
}
D1.The Endspeaker (Easy Version)
思路:先给个个人想法,用一维滚动数数组进行dp,dp[i]指dp到这位的最小费用,然后再每次数进行dp时进行双方更新,这种想法喜提WA3了,后面有时间再重新想想