标签:char pre har long codeforce min 老师 注意 img
今天看机房大佬们找老师要外网打CF比赛,我忍不住也跟着注册了个号参加了,这是我第一次打CF比赛,打得比较菜,可能还有讨论成分,请见谅哈。
题意:有$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 }
题意:有$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 }
题意:一个序列有$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 }
题意:一个序列有$n$个数。你可以合并任意两个数,即先删除两个数,再将两数相乘的值放到右边那个数的位置;同时你还有$1$次机会直接删除序列中的一个数。问做$n-1$次前两种操作后序列中剩下的唯一的数最大可以是多少。
题解:
标签:char pre har long codeforce min 老师 注意 img
原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/cf510.html