好毒的场啊,2万人只有1000左右过了3题+……
A. Potion-making
没想到被A题卡了20分钟,甚至还wa了两次(掉分绝对原因),虽然仍然是个水题,但好好分析一下引以为戒吧……
题意
给定一个百分比k,然后从0开始制药。每次可以加一滴原料或者加一滴水,问最少加几滴东西,可以让药的原料浓度为k%.(k为整数)
分析
知道应该是简单的数学题,但开局懒了不想推,于是暴力枚举,结果double不熟练一直出不了……最后还是老老实实用结论。
找到k和100的最大公因数g,然后我们就可以以g为单位,替换掉百分比里以1%为单位的计算方式,也就是1滴的贡献将变为g分之一。比如,100和6的最大公因子是2,我们就可以“一滴当两滴”,只需要50滴中有3滴原料,就满足条件了。
故答案为100÷gcd(k,100)100÷gcd(k,100)100÷gcd(k,100).
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
//#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
const double eps = 1e-4;
signed main()
{
// IOS;
int t;
cin >> t;
while(t--)
{
int k;
cin >> k;
cout << 100 / __gcd(100, k) << endl;
}
return 0;
}
B. Permutation Sort
题意
给定一个数组,每次操作可以将一段连续子序列排成有序,但选取的子序列不能是整个原数组,问最少操作几次让数组有序。
分析
由于目标是整个数组有序,而我们又不能选取整个数组,那么贪心地选,我们每次操作都选取n−1n-1n−1个元素(要么不选第一个,要么不选最后一个)
继续分析,问题就变简单了:
如果数组本来就有序,那就不操作了;
-
如果第一个元素和最后一个元素本来就是最大/最小的元素,那么只需要排中间n−2n-2n−2个元素一次。
-
如果最小元素在第一个,最大元素不在最后一个,那么最大元素一定在中间n−2n-2n−2个元素之中,我们将后n−1n-1n−1个元素排一下就可以了,操作一次。(最小元素不在第一个,最大元素在最后一个同理)
-
如果最小元素在最后,最大元素在第一个,那么它们2个都无法一次到达应该到的位置。先由一次操作选取前n−1n-1n−1个,把最大元素放到中间来,再由一次操作选取后n−1n-1n−1个把最小元素放到中间,同时把最大元素放到最后一个,最后一次操作再把最小元素放到第一个,总共三次操作。
-
剩下的情况就是:最小元素和最大元素都不在自己的位置,但也没有出现3.中的情况。这说明最小元素和最大元素都在中间。由于一次操作不能同时覆盖第一个位置后最后一个位置,我们操作2次即可。
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
const int maxn = 55;
int a[maxn];
int n;
bool sorted()
{
fors(i, 1, n - 1){
if(a[i] > a[i + 1]){
return 0;
}
}
return 1;
}
signed main()
{
IOS;
int t;
cin >> t;
while(t--)
{
cin >> n;
int mn = inf, mx = -1;
fors(i, 1, n) cin >> a[i], mn = min(mn, a[i]), mx = max(mx, a[i]);
if(sorted()){
cout << 0 << endl;
continue;
}
else{
if(a[1] == mn || a[n] == mx){
cout << 1 << endl;
}
else if(a[1] == mx && a[n] == mn){
cout << 3 << endl;
}
else cout << 2 << endl;
}
}
return 0;
}
C. Robot Collisions
题意
描述起来有点复杂呀,看开头的链接吧QAQ。
分析
分析特点:假如一个robot的初始坐标是1,另一个的坐标是3,那么不管他们开始的方向怎么样,墙的坐标怎么样,由于碰到墙会反弹,最后一定会相撞(想一想为什么 --lrj);进一步思考,如果不考虑其他robot,有一对robot坐标都为奇数,那么它们一定会相撞。
相应的,如果一对robot坐标都为偶数,那么它们也一定会相撞。
而如果一个坐标是奇数,另一个是偶数,那么它们无论如何也不会相撞,最多只会擦肩而过(一个从坐标a移动到坐标b,另一个从坐标b移动到坐标a)。
因此,我们可以将所有机器人按初始坐标分组,奇数为一组,偶数为一组。不同组的机器人之间没有任何关系。
接着,对于同一组的机器人,很容易知道:相向而行的机器人无论如何都会比同向行进的机器人先相撞。相邻的机器人,如果左边的是往右走,右边的是往左走,他们就一定相撞。把所有相邻的"RL"全部消掉,剩下的机器人的行动方向一定是"LL…LR…RR",然后,对于剩下的所有向左走的机器人,前两个会首先相撞,然后是第3、4个相撞,以此类推.最后至多剩下一个往左走的机器人。右边同理。如果向左、向右都剩下了一个机器人,那么这两个机器人也可以相撞(它们行进的路径上没有其他机器人了,最后一定相遇)。
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
int n, m;
const int maxn = 3e5 + 10;
struct node{
int x, id;
char c;
bool operator<(const node& b)const{
return x < b.x;
}
};
deque<node> a;
deque<node> b;
int ans[maxn];
void operate(deque<node> &u){
sort(u.begin(), u.end());
// 先去除所有相邻RL的.
stack<node> q;
for(int i = 0; i < u.size(); ++i){
if(u[i].c == 'R'){
q.push(u[i]);
continue;
}
if(q.empty()) continue;
ans[q.top().id] = ans[u[i].id] = (u[i].x - q.top().x) / 2;
q.pop();
}
deque<node> tmp;
for(int i = 0; i < u.size(); ++i){
if(ans[u[i].id] == -1){
tmp.push_back(u[i]);
}
}
u = tmp;
while(u.size() > 1){
node p1 = u.back(); u.pop_back();
node p2 = u.back(); u.pop_back();
if(p1.c == p2.c && p1.c == 'R'){
ans[p1.id] = ans[p2.id] = ((m - p1.x) * 2 + p1.x - p2.x) / 2;
}
else{
u.push_back(p2);
u.push_back(p1);
break;
}
}
while(u.size() > 1){
node p1 = u.front(); u.pop_front();
node p2 = u.front(); u.pop_front();
if(p1.c == p2.c && p1.c == 'L'){
ans[p1.id] = ans[p2.id] = (p2.x + p1.x) / 2;
}
else{
u.push_front(p2);
u.push_front(p1);
break;
}
}
if(u.size() == 2){
ans[u.front().id] = ans[u.back().id] = (2 * u.front().x + 2 * (m - u.back().x) + abs(u.back().x - u.front().x)) / 2;;
}
u.clear();
}
node A[maxn];
signed main()
{
IOS;
int t;
cin >> t;
while(t--)
{
cin >> n >> m;
node tmp;
fors(i, 1, n){
cin >> A[i].x;
A[i].id = i;
ans[i] = -1;
}
fors(i, 1, n){
cin >> A[i].c;
if(A[i].x & 1) a.pb(A[i]);
else b.pb(A[i]);
}
operate(a), operate(b);
fors(i, 1, n){
cout << ans[i] << ' ';
}
cout << endl;
}
return 0;
}
D. Armchairs
震惊!竟然是最小费用最大流模板题!
然而依旧可以用二维dp做出
题意
给定一个01串,要把所有的1填到0上面去,同一个0只能填一个1. 每次填数的花费为0和1
这两个位置的差值。问要把所有的1填过去,最小花费是多少。
分析
最小费用最大流我不会
直接讲dp思路吧,有点毒……这题很容易误导人往贪心想,唉。
dp[i][j]dp[i][j]dp[i][j]表示对前iii个1,把他们放到前jjj个0的最小花费。dpdpdp数组初始化为infinfinf. 然后,很显然地所有dp[0][i]dp[0][i]dp[0][i]是0. 接下来是状态转移:
对于i,ji,ji,j,我们看第iii个1到底该放哪。选择只有2个:要么放第jjj个,要么放到前j−1j-1j−1个位置的某一个。前j−1j-1j−1看成一个位置是因为我们已经知道了dp[i][j−1]dp[i][j-1]dp[i][j−1]嘛。 状态转移方程:
dp[i][j]=min(dp[i][j−1],dp[i−1][j−1]+∣pos0[j]−pos1[i]∣ dp[i][j]=min(dp[i][j-1],dp[i-1][j-1]+|pos0[j]-pos1[i]| dp[i][j]=min(dp[i][j−1],dp[i−1][j−1]+∣pos0[j]−pos1[i]∣
式中pos0[j]pos0[j]pos0[j]表示第jjj个0出现的位置,pos1[i]pos1[i]pos1[i]表示第iii个1出现的位置。
因为经过初始化,所以当i>ji>ji>j时,dpdpdp值无法更新,肯定是infinfinf。
那么答案显然是dp[num1][num0]dp[num1][num0]dp[num1][num0]了。
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
// #define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
// #define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
int n;
const int maxn = 5005;
int a[maxn]; // 1
int b[maxn]; // 0
int dp[maxn][maxn];
signed main()
{
IOS;
// int t;
// cin >> t;
// while(t--)
// {
int x;
int pa = 1, pb = 1;
cin >> n;
fors(i, 1, n){
cin >> x;
if(x) a[pa++] = i;
else b[pb++] = i;
}
memset(dp, 0x3f, sizeof(dp));
fors(i, 0, pb) dp[0][i] = 0;
fors(i, 1, pa){
fors(j, 1, pb){
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j - 1] + abs(a[i] - b[j]));
}
}
cout << dp[pa][pb] << endl;
// }
return 0;
}
本文详细分析了四道算法题目,包括制药问题的数学解法、数组排序的贪心策略、机器人碰撞的逻辑推断以及最小费用最大流的二维DP应用。通过对每道题目的深入解析,揭示了解题的关键思路和技巧。
1300

被折叠的 条评论
为什么被折叠?



