题目传送门
A. Most Unstable Array(水题)
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI acos(-1)
#define PB push_back
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define ios ios::sync_with_stdio(false);
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 65;
ll num[mxn];
int main()
{
/* fre; */
int T;
scanf("%d", &T);
while(T --)
{
int n, m;
scanf("%d %d", &n, &m);
if(n == 1)
{
printf("0\n");
}
else if(n == 2)
{
printf("%d\n", m);
}
else
printf("%d\n", 2 * m);
}
return 0;
}
B. Two Arrays And Swaps(巨水题)
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI acos(-1)
#define PB push_back
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define ios ios::sync_with_stdio(false);
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 1e5;
int ar[mxn];
int br[mxn];
int main()
{
/* fre; */
int T;
scanf("%d", &T);
while(T --)
{
int n, k;
scanf("%d %d", &n, &k);
for(int i = 1; i <= n ;i ++)
scanf("%d", &ar[i]);
for(int i = 1; i <= n; i++)
scanf("%d", &br[i]);
sort(br + 1, br + 1 + n, greater<int>());
int s = n + k;
for(int i = n + 1; i <= s; i ++)
{
ar[i] = br[i - n];
}
sort(ar + 1, ar + 1 + s, greater<int>());
int sum = 0;
for(int i = 1; i <= n; i ++)
sum += ar[i];
printf("%d\n", sum);
}
return 0;
}
C. Board Moves(一般水)
分析
- 题意
- 给我们一个 n*n(n恒为奇数)的矩阵,矩阵内放有数字,对于一次操作我们可以选中一个数把它移动到共用一条边的格子或则共用一条对角线的格子,问最少经过多少次这样操作我们可以把所有的数字都移动到一个格子中
- 思路
- 显然是把所有的数字移动到最终间的格子耗费的操作次数最少
- 对于操作的时候,我们可以选择最外围的格子中的数字,往较次外层对应的格子进行转移,之后最外层的消失,此外层变成最外层
- 重复过程3,知道所有的数都被移动到最中间的格子
- 在这期间统计操作次数就行了
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI acos(-1)
#define PB push_back
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define ios ios::sync_with_stdio(false);
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 1e5;
int ar[mxn];
int br[mxn];
int main()
{
/* fre; */
int T;
scanf("%d", &T);
while(T --)
{
ll n;
scanf("%lld", &n);
ll ans = 0, sum = 0;
while(n > 1)
{
sum += 2 * n + (n - 2) * 2;
ans += sum;
n -= 2;
}
printf("%lld\n", ans);
}
return 0;
}
D. Constructing the Array(优先级队列 模拟)
分析
- 题意
- 给我们一个长度为n元素全部为0的数组ar,在第i次操作的时候,我们先找到在ar中最长的连续0的那一个区间l,r,如果r-l+1为奇数,我们就把位于 ( l + r ) / 2 (l+r)/2 (l+r)/2的0替换成i,否则就把位于 ( l + r − 1 ) / 2 (l+r-1)/2 (l+r−1)/2位置的0替换成1,这样经过n次替换操作之后,输出我们得到的ar
- 思路
- 思路的核心就是通过优先级队列模拟填数字的过程,,我们先给优先级队列按区间长度写一个比较函数(如果长度相同按从左到右的顺序排序),之后我们就不短从 队头 取出元素(本质就是一个区间设为 [ l , r ] [l,r] [l,r]),接下来我们就要修改这个区间中间位置的值,其实我们很容易发现我们一修改这个区间的中点,就会产生这个两个子区间,把这个两个子区间在压入优先级队列中就行了,之后循环往复,知道把n个数都填完就行了
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI acos(-1)
#define PB push_back
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define ios ios::sync_with_stdio(false);
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 2e5 + 10;
int ar[mxn];
struct Node
{
int l, r;
bool operator <(const Node x) const
{
if(r - l == x.r - x.l)
return l > x.l;
return r - l < x.r - x.l;
}
} st;
priority_queue<Node> q;
int main()
{
/* fre; */
int T;
scanf("%d", &T);
while(T --)
{
while(! q.empty()) q.pop();
int n;
scanf("%d", &n);
q.push((Node){ 1, n });
int ct = 1;
while(! q.empty())
{
if(ct == n + 1) break;
st = q.top(); q.pop();
if((st.r - st.l + 1) % 2)
{
ar[(st.l + st.r) / 2] = ct ++;
q.push((Node){ st.l, (st.l + st.r)/2 - 1 });
q.push((Node){ (st.l + st.r)/2 + 1, st.r });
}
else
{
ar[(st.l + st.r - 1)/ 2] = ct ++;
q.push((Node){ st.l, (st.l + st.r - 1)/2 - 1 });
q.push((Node){ (st.l + st.r - 1)/2 + 1, st.r });
}
}
for(int i = 1; i <= n; i ++)
printf("%d ", ar[i]);
printf("\n");
}
return 0;
}
E. K-periodic Garland(贪心)
分析
- 题意
- 给我们一个长度为n 的01字符串,对于每一次操作我们可以把这个字符串中0变成1或者1变成,问现在通过过操作把字符串中所有的相邻的1都变成距离为k,问这样最少需要所少次操作
- 思路
- 贪心的思路,虽说有一点点暴力,但是的确是一个巧妙的枚举,其中看着简单的两句,但是理解还需要结合贪心的思路,,
- 这一题我们我们枚举i属于1~k,i表示我们进行操作完之后第一个1的位置(如果是这个位置原本是0的话,就没必要把它非变成1),这样我们一旦确立了第一个1的位置,那么后面所有的1的位置就确定了,剩下我们统计所给字符串中1的数量设为ct,接下来对于任意一个确定i,我们就可以遍历到所有的在字符串中应该是1的位置「i,i+k,i+2*k…」,在这个遍历过程中,对于所有的这些位置,我们要记录的是最大连续1的数量设为sum,那么cnt - sum就是我们当前枚举i的答案,最后对于所有的ans取一下最大值就行了
- 补充:为什么在遍历的过程要维护的是 最大连续1的数量sum?,维护这个是因为除了最大连续1的部分,其他部分有1的地方都是我们直接要要抛弃,把这些1变成0的,如果不抛弃这些位置的1,我们需要耗费更多的操作去吧它们跟最大连续1部分给链接上,,,请用贪心的思路去理解这个地方
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI acos(-1)
#define PB push_back
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define ios ios::sync_with_stdio(false);
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 2e6 + 10;
char ar[mxn];
int main()
{
/* fre; */
int T;
scanf("%d", &T);
while(T --)
{
int n, k;
scanf("%d %d", &n, &k);
scanf("%s", ar + 1);
int ct = 0;
for(int i = 1; i <= n; i ++)
if(ar[i] == '1') ct ++;
//枚举余数
int ans = INF;
for(int i = 1; i <= k; i ++)
{
int sum = 0, mx = 0;
for(int j = i; j <= n; j += k)
{
if(ar[j] == '1')
sum ++;
else
sum --;
sum = max(0, sum);
mx = max(mx, sum); //记录最大sum值
}
ans = min(ans, ct - mx);
}
printf("%d\n", ans);
}
return 0;
}
F. Decreasing Heights
分析
- 题意
- 给我们一个n*m的矩阵
C
(
i
,
j
)
C(i,j)
C(i,j),其中对于某个矩阵中某个位置值
C
(
a
,
b
)
C(a,b)
C(a,b)表示该位置的地形高度,先在一个人要从
(
1
,
1
)
(1,1)
(1,1)位置到
(
n
,
m
)
(n,m)
(n,m)位置,这人每次可以向下,或者向右走一格,且每次走到的格子的高度要比没走之前所在的位置的高度+1,,我们每次操作可是使矩阵中某个位置的地形的高度-1(
注意:我们可以把地形的高度减小到0,或者负数
),问我们最少需要多少次这样的操作,使这个人能够到达目的地
- 思路
-
思路非常简单,首先我们要明白我们一旦确定了 ( 1 , 1 ) (1,1) (1,1)位置出的高那么它们方格要走的数字也随之确定了,效果如下图
,由于这题的数据范围也不是太大我们可以枚举确定 ( 1 , 1 ) (1,1) (1,1)位置的高度,之后其他位置的相应高度也就确定了,之后我们再从起点位置到终点位置dp一趟(看到题目中人走的方式我们应该可以想起来dp的形式),对于某个位置可以从上方位置或者从右方位置转移过,我们当然选择两种中耗费较小的位置来转移,在这个费用的基础上我们在加上当位置需要的耗费(当前位置的高度 - 实际上这个位置应该的高度) -
接下来的问题就是,枚举确定(1,1)位置的高度了,其实我们可以选中矩阵中任意个一个数字把它作为标准高度(需要对它做稍微的改变,看具体代码),这个过程就相当于把它变成一个水平面、参考物,而矩阵中的值也要进行一些改变,是的它们相对标准高度的相对距离不变,
-
这样对于对于每个标准我高度我们都dp一下,去除其中的最小变化次数就是ans了
代码
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define endl '\n'
#define PI acos(-1)
#define PB push_back
#define ll long long
#define db double
#define INF 1e18
#define mod 998244353
#define lowbit(abcd) (abcd & (-abcd))
#define ios ios::sync_with_stdio(false);
#define fre \
{ \
freopen("A.txt", "r", stdin); \
freopen("Ans.txt", "w", stdout); \
}
const int mxn = 1e2 + 10;
ll mz[mxn][mxn];
ll dp[mxn][mxn];
map<ll, int> mp;
int n, m;
ll work(ll hight)
{
ll ans = 1e18;
for(auto x : mp)
{
ll h = x.first;
if(h > hight) continue;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= m; j ++)
{
ll H = h + (i - 1) + (j - 1);
if(i == 1 && j == 1)
dp[i][j] = hight - H;
else if(i == 1)
{
if(mz[i][j] >= H)
dp[i][j] = dp[i][j - 1] + mz[i][j] - H;
else
dp[i][j] = INF;
}
else if(j == 1)
{
if(mz[i][j] >= H)
dp[i][j] = dp[i - 1][j] + mz[i][j] - H;
else
dp[i][j] = INF;
}
else
{
if(mz[i][j] >= H)
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + mz[i][j] - H;
else
dp[i][j] = INF;
}
}
}
ans = min(ans, dp[n][m]);
}
return ans;
}
int main()
{
/* fre; */
int T;
scanf("%d", &T);
while(T --)
{
mp.clear();
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
{
scanf("%lld", &mz[i][j]);
mz[i][j] = mz[i][j] - i - j + 2;
if(mp[mz[i][j]] == 0) mp[mz[i][j]] = 1;
mz[i][j] = mz[i][j] + i + j - 2;
}
printf("%lld\n", work(mz[1][1]));
}
return 0;
}