好毒的场啊,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 ÷ g c d ( 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 − 1 n-1 n−1个元素(要么不选第一个,要么不选最后一个)
继续分析,问题就变简单了:
如果数组本来就有序,那就不操作了;
-
如果第一个元素和最后一个元素本来就是最大/最小的元素,那么只需要排中间 n − 2 n-2 n−2个元素一次。
-
如果最小元素在第一个,最大元素不在最后一个,那么最大元素一定在中间 n − 2 n-2 n−2个元素之中,我们将后 n − 1 n-1 n−1个元素排一下就可以了,操作一次。(最小元素不在第一个,最大元素在最后一个同理)
-
如果最小元素在最后,最大元素在第一个,那么它们2个都无法一次到达应该到的位置。先由一次操作选取前 n − 1 n-1 n−1个,把最大元素放到中间来,再由一次操作选取后 n − 1 n-1 n−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思路吧,有点毒……这题很容易误导人往贪心想,唉。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示对前 i i i个1,把他们放到前 j j j个0的最小花费。 d p dp dp数组初始化为 i n f inf inf. 然后,很显然地所有 d p [ 0 ] [ i ] dp[0][i] dp[0][i]是0. 接下来是状态转移:
对于 i , j i,j i,j,我们看第 i i i个1到底该放哪。选择只有2个:要么放第 j j j个,要么放到前 j − 1 j-1 j−1个位置的某一个。前 j − 1 j-1 j−1看成一个位置是因为我们已经知道了 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j−1]嘛。 状态转移方程:
d p [ i ] [ j ] = m i n ( d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] + ∣ p o s 0 [ j ] − p o s 1 [ 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]∣
式中 p o s 0 [ j ] pos0[j] pos0[j]表示第 j j j个0出现的位置, p o s 1 [ i ] pos1[i] pos1[i]表示第 i i i个1出现的位置。
因为经过初始化,所以当 i > j i>j i>j时, d p dp dp值无法更新,肯定是 i n f inf inf。
那么答案显然是 d p [ n u m 1 ] [ n u m 0 ] 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;
}