奋斗群群赛9总结与心得

文章目录


#总体感受
https://cn.vjudge.net/contest/185017
树不会,第2题一直WA,第5题看不懂,大概也就这样了。
#T1
##题目
对于输入的n个正整数1-n,当看到未输出的最大值时,输出之,然后同时输出已经出现比它小1的值直到该值未出现为止,对于每一行,输出一个回车。
##思路
本题标算就是暴力。

#include<bits/stdc++.h>
using namespace std;
int a[100010];bool b[100010];
int main()
{
int n,i;
cin>>n;
int k=n;
for (i=1;i<=n;i++) cin>>a[i];
for (i=1;i<=n;i++) 
  {
  b[a[i]]=1;
  while (b[k]==1) {cout<<k<<" ";k--;}
  cout<<endl;
  } 
}	

#T2
##题目
你去办护照,护照办理的服务时间是第ts个时间单位到第tf个时间单位(如果你在第tf个时间单位到服务点,他是不给你办护照的)。每个人办护照的时间是t个时间单位。你已经知道有多少人在什么时候去服务点排队,输出一个时间,使得你在这个时间去排队所等待的时间最少。
##思路
随输随判。当时一上来看错题目各种特判各种WA。

#include<bits/stdc++.h>
using namespace std;
int main()
{
long long ts,tf,t,n,a,minwaitingtime=9999999999999999,answer;
cin>>ts>>tf>>t>>n;
while (n--)
  {
  scanf("%I64d",&a);
  if (a&&a<=tf-t) //我在这个时间之前去排队能给我办理,否则肯定办不了是没有意义的
    {
    if (max(ts,a-1)<=tf-t&&ts-a+1<minwaitingtime)//考虑等待时间最少
      {
      minwaitingtime=ts-a+1;answer=min(a-1,ts);
	  }
	ts=max(ts,a)+t;//让上一个结束的时间变成下一个开始的时间
	}
  }
if (ts<=tf-t) answer=ts;//如果最后排队的人办完了护照还有时间服务的,你就在下一个时间去即可。
cout<<answer;
}

#T3
##题目
一棵树的每个结点上有一个数字,如果能把树剪两刀,使得三部分所有结点的和相等,输出剪开的是哪两个结点上面的线,若不能,输出“-1”。
##思路
把结点的和sum三分,如果不是整数直接-1伺候。从叶结点开始搜,比sum小,则把该结点清零,把值加到该结点的父节点,继续搜过去,直到该结点的值等于sum,找第二个切割点,如果能找到两个切割点就输出之,如果只能找到一个,还是-1伺候。

#include<bits/stdc++.h>
using namespace std;
const int boss=1000000;
int n,num[boss+10],l,first[boss+10],hao=0,submit,judge=0,m,root;
struct line {
  int next,to;
}bian[boss];
void add(int a,int b)//建立邻接表
{
hao++;
bian[hao].to=b;
bian[hao].next=first[a];
first[a]=hao;
}
void dfs(int p)
{
int q=p;
p=first[p];
while (p!=-1)
  {
  dfs(bian[p].to);
  num[q]+=num[bian[p].to];
  p=bian[p].next;
  }
if (num[q]==submit&&q!=root) if (judge==0)
  {
  judge++;
  num[q]=0;
  m=q;
  }
else
  {
  judge++;
  printf("%d %d",m,q);
  exit(0);//这个操作可以直接退出程序
  }
}
int main()
{
int i;
cin>>n;
memset(first,-1,sizeof(first));
for (i=1;i<=n;i++)
  {
  scanf("%d%d",&l,&num[i]);
  if (l==0) root=i;
  else if (l!=0) add(l,i);
  submit+=num[i]; 
  }
if (submit%3!=0) {cout<<"-1";return 0;}
else submit/=3;
dfs(root);
if (judge!=2) cout<<"-1";
}

#T4
##题目
某人每天要喝K瓶牛奶,她家里冰箱有n瓶牛奶,超市里有m瓶牛奶。每瓶牛奶有保质期,0就是当天过期,1就是明天过期,以此类推。给出每瓶牛奶的保质期,求她在不让牛奶过期的情况下能够在超市购买的最多牛奶的瓶数以及牛奶的编号。如果她连自己家里冰箱的牛奶也喝不完,输出“-1”。
##思路
在3个小时30分钟内我是全场唯一A了这道题的同志,然而我写的代码还是有漏洞。后来我去CF上找别人的代码,那个最短的和我的想法一样,居然连漏洞都和我一样!
首先用一个桶存放家里冰箱的牛奶的保质期,然后当天喝当天过期的牛奶(漏洞就在这里,弄不好会被hack掉),如果处理方法和商店里的牛奶一样就没有问题了。接下来用一个pair数组存储商店里的牛奶的保质期和编号,用一个sort进行排序,从头开始暴枚。如果是这样铁定TLE,所以我进行了一步神操作。具体请看代码,注意p这个变量在中间的运用。

#include<bits/stdc++.h>
using namespace std;
pair<int,int> sm[1000010];
int n,m,k,tong[10000010]={0},sc[1000010];
int main()
{
int a,i,j,t=0,p=0;
cin>>n>>m>>k;
for (i=1;i<=n;i++)
  {
  scanf("%d",&a);
  tong[a]++;
  } 
for (i=0;i<=10000000;i++) 
  {
  tong[i]=k-tong[i];//当天还能再喝多少瓶
  if (tong[i]<0) {cout<<"-1";return 0;}//当天牛奶不够喝,直接炸
  } 
for (i=1;i<=m;i++) 
  {
  scanf("%d",&sm[i].first);
  sm[i].second=i;
  }
sort(sm+1,sm+m+1);
for (i=1;i<=m;i++)
  {
  for (j=p;j<=sm[i].first;j++) if (tong[j]>0)//在保质期之前找到一个能喝牛奶的日子,p是这之前第一个能喝牛奶的日子 
    {    
	tong[j]--;//买下这瓶牛奶
    if (tong[j]==0) while (tong[p]==0) p++;//扫的时候直接从第一个能喝牛奶的日子开始扫,避免重复运算已经喝完牛奶的日子
	t++;
	sc[t]=sm[i].second;
	break;
	}
  }
printf("%d\n",t);
for (i=1;i<=t;i++) printf("%d ",sc[i]);
}

#T5
##题目
我有无数张100元的毛爷爷,但是我只有m个一元的硬币。现在我要去买东西,售货员不喜欢找钱,如果让他找钱,在第i天找x元钱会让他获得x*w[i]点的怒气值。输入天数n,硬币数m,每天我要花的钱数a[i]以及怒气系数w[i],求你在第n天买完东西之后售货员的怒气系数的最小值,你每天付的百元大钞的数量以及硬币的数量(假设你被找的钱全部都是一元的硬币)
##思路
看起来是dp,实际上直接贪心就搞定了。借鉴yyk大佬的代码,建立小根堆,对每天都按照标准付钱,每次找出最小的怒气值,出堆。具体看代码。代码里用的是set。

#include<bits/stdc++.h>
using namespace std;
const int boss=100000;
set<pair<long,long> > s;
long long note[boss+10],coin[boss+10];
int main()
{
long long n,m,a,i,answer=0;
cin>>n>>m;
for (i=1;i<=n;i++) 
  {
  scanf("%I64d",&coin[i]);
  note[i]=coin[i]/100;//这一天用纸币的数量 
  coin[i]%=100;//这一天用硬币的数量 
  }
for (i=1;i<=n;i++)
  {
  m-=coin[i];//看看硬币能不能付得起 
  scanf("%I64d",&a);
  if (coin[i]) s.insert(make_pair(a*(100-coin[i]),i));//把怒气值放入集合 
  if (m<0)//硬币付不起要找钱
    {
    m=m+100;//找钱
    answer+=s.begin()->first;//加上最小的怒气值 
    note[s.begin()->second]++;//多花一张纸币 
    coin[s.begin()->second]=0;//硬币就不用花了 
    s.erase(s.begin());//重新计算第2小的怒气值
	}
  }
printf("%I64d\n",answer);
for (i=1;i<=n;i++) printf("%I64d %I64d\n",note[i],coin[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值