比赛的记录。
废话不多说。
-----------------------------------------------------------------------------
A. Potion-making
思路:配魔法药水,要求魔法精华的浓度为k。t组样例,每组样例给你k值,输出要配出浓度的为k的魔法药水的魔药和水的量的最小和。
就输出 k + (100-k) ,如果k和(100-k)不互质就同除以最大公约数。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
int gcd(int x,int y){
return y?gcd(y,x%y):x;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;cin>>t;
while(t--){
int k;cin>>k;
int water=100-k;
int cho=gcd(water,k);
if(cho!=1){
water/=cho;k/=cho;
}
cout<<water+k<<endl;
}
}
-----------------------------------------------------------------------------
B. Permutation Sort
思路:给你一个数组,要求你给这个数组从小到大排序,每次可以选k个元素排序,注意k不能等于n,然后问你最少的排序次数是多少次。
既然不能给n个元素直接排序,我们可以给n-1个元素排序。
给 1 到 n-1 的元素排序 。排完后,如果 第 n 个元素 大于 排序完成后 第 n-1 个元素,那么这个数组就排完了 ,次数为1。如果 第 n 个元素小于排序后的第 n-1 个元素但是大于第 1 个元素,那么对 2到 n再排序一次,数组就排完了,这时次数为2。最后一种情况,如果第 n 个元素小于排序后的第 n-1 个元素并且小于第 1 个元素,这时给2到n排序一次,再给 1 到 n-1 排序一次,数组就完成了,这时次数为3。
同理按照数组对称性,我们一开始是给 1 到 n-1 排序的,也可以是给 2 到 n 排序,这时按照上面的做法,检查第 1 个元素 值的大小就可以了。
选取这2种方案中次数最小的方案。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int N=55;
int ar[N];
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;cin>>t;
while(t--){
memset(ar,0,sizeof(ar));
int n;cin>>n;
int ok=0;
int fro_max=-1,end_min=0x3f3f3f3f;
int fro_min=0x3f3f3f3f,end_max=-1;
for(int i=1;i<=n;i++){
cin>>ar[i];
if(ar[i]<ar[i-1])ok=1;
if(i>1)end_min=min(end_min,ar[i]),end_max=max(end_max,ar[i]);
if(i<n)fro_max=max(fro_max,ar[i]),fro_min=min(fro_min,ar[i]);
}
if(!ok)cout<<0<<endl;
else{
if(fro_max<=ar[n]||end_min>=ar[1])cout<<1<<endl;
else{
if(fro_min>ar[n]&&end_max<ar[1])cout<<3<<endl;
else cout<<2<<endl;
}
}
}
}
-----------------------------------------------------------------------------
C. Robot Collisions
没写出来,我看别的大佬的思路学会的。
思路:在一段长度有限的坐标轴上,有n只蚂蚁,它们起始分别在不同的点上,每只蚂蚁最开始都有一个起始的运动方向,如果碰到墙壁就会往回走,每秒走一步。
如果两个蚂蚁在整数点相遇,它们两个就会爆炸。然后就没了。要求你输出每只蚂蚁在什么时候爆炸,如果不会爆炸就输出-1。
注意,上面的蚂蚁只会在整数点相遇才会爆炸其实让这题变简单了。 如果一只蚂蚁的起始点是偶数,那么可以由简单的推导得知,它只会与同样起始点是偶数的蚂蚁相遇。同理,奇数也只会遇见奇数。因此可以把数组中奇数和偶数的蚂蚁分开处理。
难点就是怎么处理蚂蚁爆炸的时间。
假设现在有3只蚂蚁X、Y、Z。它们一开始都在偶数(或者奇数)点上。从左往右看,如果X一开始向右走,Y一开始向左走,那么Y一定先和X相撞,无论Z向什么方向走。如果一开始X向左走,Y也向左走,那么那么Y也一定先和X相撞,无论Z向什么方向走。但是如果不是上面这两种情况,比如X向左,Y向右走,那么Y有可能先和Z相撞,也有可能先和X相撞,这取决Z的方向。
按照这个思路走,用一个stack来存蚂蚁,如果stack顶部的蚂蚁和现在的蚂蚁是前两种情况, 那么让stack顶部的蚂蚁出栈,现在的蚂蚁不入栈,就算是这两只蚂蚁爆炸了。否则,就让现在的蚂蚁入栈,等之后再处理这只蚂蚁。
同理,奇数蚂蚁和偶数蚂蚁分开来一遍这个流程就行了。
如果理解了这题的想法,就发现这题其实不难,但是挺难写的(不难,但是难)。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int N=3e5+7;
struct node{
int id,dis,dir;
//结构体内嵌比较函数
bool operator <(const node &tmp)const{
return dis<tmp.dis;
}
}ar[N];
// stack,even用来存偶数蚂蚁,odd用来存奇数蚂蚁
stack<node> even,odd;
int cnt[N]; // cnt用来存蚂蚁爆炸时间
int main()
{
ios::sync_with_stdio(false);
int t;cin>>t;
while(t--){
int n,m;cin>>n>>m;
if(!even.empty())even.pop();
if(!odd.empty())odd.pop();
for(int i=1;i<=n;i++){
cin>>ar[i].dis;
ar[i].id=i;
}
for(int i=1;i<=n;i++){
char ch[2];cin>>ch;
if(ch[0]=='L')ar[i].dir=1; // dir为1表示这只蚂蚁向左
else if(ch[0]=='R')ar[i].dir=0; // 否则表示向右走
}
sort(ar+1,ar+1+n);
memset(cnt,-1,sizeof(cnt)); //不会爆炸就输出-1
for(int i=1;i<=n;i++){
if((ar[i].dis)%2==1){
if(odd.empty())odd.push(ar[i]);
else{
node tmp=odd.top();
// 第一种情况,两只相邻的蚂蚁都向左走
if(tmp.dir==1&&ar[i].dir==1){
cnt[tmp.id]=tmp.dis+abs(tmp.dis-ar[i].dis)/2;
cnt[ar[i].id]=cnt[tmp.id];
odd.pop();
}
// 第二种情况,一只向右,一只向左
else if(tmp.dir==0&&ar[i].dir==1){
cnt[tmp.id]=abs(tmp.dis-ar[i].dis)/2;
cnt[ar[i].id]=cnt[tmp.id];
odd.pop();
}
// 两种都不是的话,就把这只蚂蚁后来再处理
else{
odd.push(ar[i]);
}
}
}
if((ar[i].dis)%2==0){
if(even.empty())even.push(ar[i]);
else{
node tmp=even.top();
if(tmp.dir==1&&ar[i].dir==1){
cnt[tmp.id]=tmp.dis+abs(tmp.dis-ar[i].dis)/2;
cnt[ar[i].id]=cnt[tmp.id];
even.pop();
}
else if(tmp.dir==0&&ar[i].dir==1){
cnt[tmp.id]=abs(tmp.dis-ar[i].dis)/2;
cnt[ar[i].id]=cnt[tmp.id];
even.pop();
}
else{
even.push(ar[i]);
}
}
}
}
//如果栈里还有两只或以上的蚂蚁,就处理。
while(odd.size()>=2){
node next=odd.top();odd.pop();
node fro=odd.top();odd.pop();
if(next.dir==0&&fro.dir==0){
cnt[next.id]=(m-next.dis)+abs(next.dis-fro.dis)/2;
cnt[fro.id]=cnt[next.id];
}
else if(next.dir==0&&fro.dir==1){
cnt[next.id]=((m-next.dis)+fro.dis+m)/2;
cnt[fro.id]=cnt[next.id];
}
}
while(even.size()>=2){
node next=even.top();even.pop();
node fro=even.top();even.pop();
if(next.dir==0&&fro.dir==0){
cnt[next.id]=(m-next.dis)+abs(next.dis-fro.dis)/2;
cnt[fro.id]=cnt[next.id];
}
else if(next.dir==0&&fro.dir==1){
cnt[next.id]=((m-next.dis)+fro.dis+m)/2;
cnt[fro.id]=cnt[next.id];
}
}
for(int i=1;i<=n;i++)cout<<cnt[i]<<' ';
cout<<endl;
}
}
-----------------------------------------------------------------------------
D. Armchairs
一样不会,看大佬的。
我找不到那位佬的链接了,sorry。
思路:给你一个元素为0或1的有n个元素的数组,要求把这个数组上的1移到0的位置。保证1的元素数量小于n/2。
求把每个1移走的最小步骤的和。
分别把每个1、0用两个数组存下它们的位置,然后dp。
dp[ i ] [ j ] 表示把前 i 个1 移到 前 j 个0 所用的最小步骤。
可以得到递推式为:dp[ i ][ j ]=min(dp[ i ][ j-1 ],dp[ i-1 ][ j-1 ]+abs( hash_1[ i ] - hash_0[ j ]) )。
hash_1 表示了元素1的位置 , hash_0 表示了元素0的位置。
可以这样理解,要么把第 i 个 1 移到第 j 个0 上,要么就不移动保持不变,选取移动数小的方案。
注意如果 i 为1 时,这个式子有点小小的改变:dp[ i ][ j ]=min( dp[ i ][ j-1 ],abs(hash_1[ i ] - hash_0[ j ]) )
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int N=5500;
int tot_1,tot_0;
int hash_1[N],hash_0[N],dp[N][N];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int n;cin>>n;
for(int i=1;i<=n;i++){
int x;cin>>x;
if(x==1)hash_1[++tot_1]=i;
else hash_0[++tot_0]=i;
}
if(tot_1==0)cout<<0<<endl;
else{
memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=1;i<=tot_1;i++){
for(int j=i;j<=tot_0;j++){
if(i==1)dp[i][j]=min(dp[i][j-1],abs(hash_1[i]-hash_0[j]));
else dp[i][j]=min(dp[i][j-1],dp[i-1][j-1]+abs(hash_1[i]-hash_0[j]));
}
}
cout<<dp[tot_1][tot_0]<<endl;
}
}
-----------------------------------------------------------------------------