acm暑期集训_2020.07.09

acm暑期集训_2020.07.09

任务清单

1、补Codeforces Round #552 (Div. 3) E、F、G题
2、补完Codeforces Round #582 (Div. 3)

Codeforces Round #552—E、Two Teams(模拟链表)

题目链接:https://codeforces.com/contest/1154/problem/E
AC代码,没有掌握模拟链表的精髓,比较暴力

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct pp{
 int x;
 int inx;
 bool operator<(pp b){
  return x>b.x;
 }
}a[200007];
int ans[200007],L[200007],R[200007];
int main(){
 scanf("%d%d",&n,&k);
 for(int i=0;i<n;i++){
  scanf("%d",&a[i].x);
  a[i].inx=i;
  L[i]=i-1;
  R[i]=i+1;
 }
 sort(a,a+n);
 int now=1;
 for(int i=0;i<n;i++){
  int inx=a[i].inx;
  if(ans[inx]) continue;
  int cnt=0;
  int l=L[inx];
  int r=R[inx];
  ans[inx]=now;
  while(l>=0&&cnt<k){
   while(l>=0&&ans[l]){//找到满足条件的l
    l=L[l];
   }
   if(l>=0){
    ans[l]=now;
//    l=L[l];
    cnt++;
   }
  }
  cnt=0;
  while(r<n&&cnt<k){
   while(r<n&&ans[r]){//找到满足条件的l
    r=R[r];
   }
   if(r<n){
    ans[r]=now;
//    r=R[r];
    cnt++;
   }
  }
  L[inx]=l;//乱写ing,只是在改变了已经从链表中删除的值,毫无卵用,导致每次去找满足条件的r和l时,还要用一个while循环,复杂度上去了,而且写得繁琐,没有实现用链表的好处。
  R[inx]=r;
  if(now==1) now=2;
  else now=1;  
  
 }
 for(int i=0;i<n;i++){
//  if(i) printf(" ");
  printf("%d",ans[i]);
 }
 printf("\n");
 
}

改进后的代码:

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct pp{
 int x;
 int inx;
 bool operator<(pp b){
  return x>b.x;
 }
}a[200007];
int ans[200007],L[200007],R[200007];
int main(){
 scanf("%d%d",&n,&k);
 for(int i=1;i<=n;i++){//因为数组有用到下标操作,所以从1开始更容易写 
  scanf("%d",&a[i].x);
  a[i].inx=i;
  L[i]=i-1;
  R[i]=i+1;
 }
 sort(a+1,a+n+1);
 int now=1;
 for(int i=1;i<=n;i++){
  int inx=a[i].inx;
  if(ans[inx]) continue;
  int cnt=0;
  int l=L[inx];
  int r=R[inx];
  ans[inx]=now;
  while(l>0&&cnt<k){
   ans[l]=now;
   l=L[l];
   cnt++;
  
  }
  cnt=0;
  while(r<=n&&cnt<k){
   ans[r]=now;
   r=R[r];
   cnt++;
  }
  R[l]=r;//改变没有删除的数的左右边界 
  L[r]=l;//通过这样的操作,就可以形成一个新的链表,使得剩余的数又连了起来 
  if(now==1) now=2;
  else now=1;  
  
 }
 for(int i=1;i<=n;i++){
  printf("%d",ans[i]);
 }
 printf("\n");
 
}

心得反思:如果题目中主要运用到增、删操作可以考虑用链表实现,因为链表实现的话复杂度最低,这道题还可以用线段树做,也可以转换成线段树区间覆盖问题。

Codeforces Round #552—F. Shovels Shop (贪心+dp)

思路:我们把选出来k件最便宜的商品中,从小到大排个序,此时满减最优惠的就是在相邻的商品中使用,因为它每次只能减掉最便宜的,此时这样的话就最优。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
int n,k,m,x,y;
ll sum[200007],a[200007];
int c[200007];
ll dp[200007];
int main(){
 scanf("%d%d%d",&n,&m,&k);
 for(int i=1;i<=n;i++){
  scanf("%lld",&a[i]);
 }
 for(int i=0;i<m;i++){
  scanf("%d%d",&x,&y);
  c[x]=max(c[x],y);//用数组c来记录满x件时,可以减免的最大数量
 }
 sort(a+1,a+n+1);
 for(int i=1;i<=k;i++){
  sum[i]=sum[i-1]+a[i];
//  printf("%d : %lld\n",i,sum[i]);
 }
 memset(dp,0x3f3f3f3f,sizeof(dp));
 dp[0]=0;
 for(int i=1;i<=k;i++){
  dp[i]=min(dp[i],dp[i-1]+a[i]);
  for(int j=0;j<=i;j++){
   dp[i]=min(dp[i],dp[j]+sum[i]-sum[j]-(sum[j+c[i-j]]-sum[j]));
   //在连续子序列 [j+1,i]中,使用满减活动和不使用取最小花费
   //利用前缀和来实现每段的求和,sum[i]-sum[j]即区间【j+1,i】的总花费
   //可以减掉c[i-j]数量的价格,即需要减掉区间【j+1,j+c[i-1]】的总花费
  }
 }
 printf("%lld\n",dp[k]);
}

Codeforces Round #552—G. Minimum Possible LCM (思维+枚举)

思路:

LCM(a,b) = a * b / gcd(a,b);若x1、x2、x3、x4…都含有最大公因子d,且x1<x2<x3<x4…
当d是x1、x2的最大公因子则:LCM(x1,x2)=x1x2/d<LCM(x1,x4)
若d不是x1、x2的最大公因子则:LCM(x1,x2)<x1
x2/d<LCM(x1,x4)
则我们就可以枚举公因子i,取前两项就可以求得整个序列的最小LCM的值。
取前两个肯定是当前因子最小的情况,枚举完每个因子,就能得到整体最小的LCM的值。
如果找不到两个数拥有一个相同的最大公因子,则ans就是最小的两个数的乘积了,此时他们的最大公因子是1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
int n;
const int maxn=1e7+7;
struct pp{
 int x;
 int inx;
 bool operator<(pp b){
  return x<b.x;
 }
}a[maxn];
int cnt[maxn],Inx[maxn];
ll lcm(ll a,ll b){
 return a/__gcd(a,b)*b;
}
int main(){
// printf("%lld\n",lcm(10000000,9999000));
 scanf("%d",&n);
 int inx1=-1;
 int inx2=-1;
 ll Min=1ll*maxn*maxn;//int*int还是int,要强转为long long,要不是看到错误样例,我死也不会知道是这里错了
 for(int i=0;i<n;i++){
  scanf("%d",&a[i].x);
  a[i].inx=i;
  cnt[a[i].x]++;
  if(cnt[a[i].x]>=2&&a[i].x<Min){
   Min=a[i].x;
   inx1=i;
   inx2=Inx[a[i].x]; 
  }
  Inx[a[i].x]=i;
 }
 ll Lcm,A,B;
 for(int i=1;i<maxn;i++){
  int cc=0;
  for(int j=i;j<maxn;j+=i){
   if(!cnt[j]) continue;
   if(cc==0){
    A=j;
    cc++;
   }
   else if(cc==1){
    B=j;
    Lcm=lcm(A,B);
    if(Lcm<Min){
     Min=Lcm;
     inx1=Inx[A];
     inx2=Inx[B];
//     printf("%d %d %d %d\n",i,j,A,B);
    }
    cc++;
   }
   else if(cc==2){
    break;
   }
  }
 }
 if(inx1==-1&&inx2==-1){
  sort(a,a+n);
  inx1=a[0].inx;
  inx2=a[1].inx;
 }
 if(inx1>inx2) swap(inx1,inx2);
 printf("%d %d\n",inx1+1,inx2+1);
 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值