码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces Round #510

时间:2018-09-17 21:28:03      阅读:180      评论:0      收藏:0      [点我收藏+]

标签:char   pre   har   long   codeforce   min   老师   注意   img   

今天看机房大佬们找老师要外网打CF比赛,我忍不住也跟着注册了个号参加了,这是我第一次打CF比赛,打得比较菜,可能还有讨论成分,请见谅哈。

传送门

T1:Benches

题意:有$n$个长凳,初始每个长凳上面坐着$a_i$个人,现在又来了m个人,每个人会选一个长凳坐下。设新来的m个人都坐下后,人数最多的一个长凳的人数为$k$,求$k$的最小值和最大值。

题解:$k$最小:把新来的人尽量往人少的长凳上放,如果放到所有长凳人数都相等的话就均分剩下的人;$k$最大:把m个人都放到初始人数最多的长凳上。

技术分享图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int read(){
 4     int x=0; bool f=1; char c=getchar();
 5     for(;!isdigit(c);c=getchar()) if(c==-) f=0;
 6     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^0);
 7     if(f) return x;
 8     return 0-x;
 9 }
10 int n,m,a[10005],mx,cha_sum;
11 int main(){
12     n=read(),m=read();
13     for(int i=1;i<=n;i++) mx=max(mx,a[i]=read());
14     for(int i=1;i<=n;i++) cha_sum+=mx-a[i];
15     printf("%d %d\n", m-cha_sum>0 ? mx+(m-cha_sum+(n-1))/n : mx, mx+m);
16     return 0;
17 }
View Code

T2:Vitamins

题意:有$n$瓶果汁,第i瓶的价格为$c_i$,果汁中可能包含A, B, C三种维生素。问喝齐A, B, C三种维生素所需的买果汁的最小价钱。

题解:

状态压缩3种维生素的8种存在情况,dp即可。

转移就是从其它各种能够转移的情况转移过来。我写的比较粗暴,直接设了三维状态表示每种维生素是否存在。

“从其它各种能够转移的情况转移过来”主要是因为题目只要求喝齐三种维生素,没要求每种只能喝一个,因此一种维生素可以喝多个,即存在某种维生素的状态依然可以转移到存在这种维生素的状态。

我的转移判断大致是这样:当遍历到包含某种维生素的饮料时,没有这种维生素的情况能转移到有这种维生素的情况,有这种维生素的情况也能转移到有这种维生素的情况;而遍历到不包含某种维生素的饮料时,没有这种维生素的情况能转移到没有这种维生素的情况,有这种维生素的情况也能转移到有这种维生素的情况。

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define N 1001
 3 using namespace std;
 4 int inf;
 5 inline int read(){
 6     int x=0; bool f=1; char c=getchar();
 7     for(;!isdigit(c);c=getchar()) if(c==-) f=0;
 8     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^0);
 9     if(f) return x;
10     return 0-x;
11 }
12 int n,a,b[3],len,dp[2][2][2];//dp[i][j][k]
13 char c[5];
14 int main(){
15     memset(dp,0x7f,sizeof dp);
16     inf=dp[0][0][0];
17     dp[0][0][0]=0;
18     n=read();
19     int i,j,k,p;
20     for(i=1;i<=n;i++){
21         a=read(); scanf("%s",c);
22         len=strlen(c);
23         b[0]=b[1]=b[2]=0;
24         for(j=0;j<len;j++) b[c[j]-A]=1;
25         for(j=0;j<2;j++)
26             for(k=0;k<2;k++)
27                 for(p=0;p<2;p++)
28                     if(j>=b[0] && k>=b[1] && p>=b[2]){ //对应维生素数量够才能转移 
29                         dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k-b[1]][p-b[2]]+a);
30                         if(b[0]) dp[j][k][p]=min(dp[j][k][p],dp[j][k-b[1]][p-b[2]]+a);
31                         if(b[1]) dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k][p-b[2]]+a);
32                         if(b[2]) dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k-b[1]][p]+a);
33                         if(b[0] && b[1]) dp[j][k][p]=min(dp[j][k][p],dp[j][k][p-b[2]]+a);
34                         if(b[0] && b[2]) dp[j][k][p]=min(dp[j][k][p],dp[j][k-b[1]][p]+a);
35                         if(b[1] && b[2]) dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k][p]+a);//printf("7:%d\n",dp[j][k][p]);
36                     }
37     }
38     if(dp[1][1][1]==inf) printf("-1\n");
39     else printf("%d\n",dp[1][1][1]);
40     return 0;
41 }
View Code

T3:Array Product

题意:一个序列有$n$个数。你可以合并任意两个数,即先删除两个数,再将两数相乘的值放到右边那个数的位置;同时你还有$1$次机会直接删除序列中的一个数。问做$n-1$次前两种操作后序列中剩下的唯一的数最大可以是多少。

题解:

找规律题,很容易发现对于普通的合并操作,如果合并的数都不变,最终相乘合并的结果与合并顺序无关。而只有“直接删除一个数”操作能修改一次合并的数,于是这个操作怎么用就是关键。

首先,如果只用合并操作,在序列中有奇数个负数的情况下,最终乘积是负数。删去最大(绝对值最小)的一个负数,即让剩下的数的乘积变为正数 且 让所有负数的乘积为正数并最大 肯定更优;

其次,如果只用合并操作,在序列中存在0的情况下,最终乘积是0。删去这些0,让剩下的数的乘积变为正数 肯定更优。

但是第二种情况如何删除所有的0?另外上面两种情况同时存在怎么办?

考虑合并解决这些不需要的数,把这些要删除的数都相乘合并起来,然后一次删除它们合并出的数。这样就完美利用合并操作解决了只能进行一次删除操作的限制。

注意只有一个负数、全是0等极端情况,这些情况下千万不要多输出一步操作(比如序列的n个数都是0,你合并了n-1次0后又删了一次0,总共变成了n次)。

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define INF 2147483647
 4 #define MAXN 100+100
 5 #define rep(i,s,t) for(int i=s;i<=t;++i)
 6 #define dwn(i,s,t) for(int i=s;i>=t;--i)
 7 
 8 using namespace std;
 9 
10 const int maxn=1000000+1010;
11 
12 inline int read(){
13     int x=0,f=1;char ch=getchar();
14     while(!isdigit(ch)&&ch!=-)ch=getchar();
15     if(ch==-)f=-1,ch=getchar();
16     while(isdigit(ch))x=(x<<1)+(x<<3)+ch-0,ch=getchar();
17     return x*f;
18 }
19 
20 inline void write(int x){
21     int f=0;char ch[20];
22     if(!x){putchar(0),putchar(\n);return;}
23     if(x<0)x=-x,putchar(-);
24     while(x)ch[++f]=x%10+0,x/=10;
25     while(f)putchar(ch[f--]);
26     putchar(\n);
27 }
28 
29 int n,a[maxn],tot,l[maxn],tot1,xy0=-2147483646,xy1=-1;
30 bool book[maxn];
31 int main(){
32     n=read();
33     for(int i=1;i<=n;i++){
34        a[i]=read();
35        if(a[i]==0)l[++tot]=i;
36        if(a[i]<0){
37                tot1++;
38            if(xy0<a[i])xy0=a[i],xy1=i;
39        }
40     }
41     int m=n;
42     if(n==1)return 0;
43     if(tot1%2==1){
44         if(tot>=1)printf("1 %d %d\n",xy1,l[1]);
45         else {
46             printf("2 %d\n",xy1);
47         } n--;
48         book[xy1]=1;
49         if(n==1)return 0;
50     }
51     for(int i=1;i<tot;i++){
52         printf("1 %d %d\n",l[i],l[i+1]);    
53         book[l[i]]=1; n--;
54         if(n<=1)return 0;
55     }
56     if(tot>=1){n--;book[l[tot]]=1;printf("2 %d\n",l[tot]);}
57     if(n<=1)return 0; queue<int>q;
58     for(int i=1;i<=m;i++)if(!book[i])q.push(i);
59     while(!q.empty()){
60         if(n<=1)return 0;
61         int u=q.front();
62         q.pop();
63         n--;
64         int x1=q.front();
65         printf("1 %d %d\n",u,x1);
66     }
67     return 0;
68 }
View Code

 

T4:Petya and Array

题意:一个序列有$n$个数。你可以合并任意两个数,即先删除两个数,再将两数相乘的值放到右边那个数的位置;同时你还有$1$次机会直接删除序列中的一个数。问做$n-1$次前两种操作后序列中剩下的唯一的数最大可以是多少。

题解:

Codeforces Round #510

标签:char   pre   har   long   codeforce   min   老师   注意   img   

原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/cf510.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!