MZOJ 2018-03-31 考试随想
第一道题,先是题看错了,贪心的时候没有贪心对,改了1mol,最后才改对,预计正常用时20min。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int maxn=10000+10; 9 const long long mods=1000000009; 10 11 int n; 12 long long t=0; 13 long long ans=0; 14 15 struct node{ 16 int x,y; 17 }peo[maxn]; 18 19 bool cmp(node a,node b) 20 { 21 return a.x*b.y < a.y*b.x; 22 } 23 24 void readdate() 25 { 26 int ai,bi; 27 scanf("%d",&n); 28 for(int i=1;i<=n;i++) 29 { 30 scanf("%d%d",&ai,&bi); 31 peo[i].x=ai; 32 peo[i].y=bi; 33 } 34 sort(peo+1,peo+1+n,cmp); 35 return ; 36 } 37 38 void work() 39 { 40 for(int i=1;i<=n;i++) 41 { 42 ans=((ans+peo[i].x+peo[i].y*ans)+mods)%mods; 43 } 44 cout<<ans<<endl; 45 } 46 47 int main() 48 { 49 readdate(); 50 work(); 51 return 0; 52 }
第二道题也十分简单,不就是一个普通的加减值问题吗?
但是,我首先想到的是用公式
设g1为大于答案值的个数,g2是小于答案值的个数,s1是大于答案的每个数的总和,s2是小于答案的每个数的总和。同时,答案为x,能量损失为a。
则有:(s1-h1*x)*a = h2*x - s2
样例过了,但是好像有精度上的问题。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int maxn=10000+10; 9 10 int n,k; 11 int shu[maxn]; 12 int sum; 13 int sum1,sum2; 14 int g1,g2; 15 double pin,ans; 16 17 void readdate() 18 { 19 cin>>n>>k; 20 for(int i=1;i<=n;i++) 21 { 22 scanf("%d",&shu[i]); 23 sum+=shu[i]; 24 } 25 sort(shu+1,shu+n+1); 26 pin = (sum*1.0) / (n*1.0); //我们可以轻易证得,答案 < 所有数加起来的平均数,(因为有能量损失) 27 for(int i=1;i<=n;i++) 28 { 29 if(shu[i]-pin>=0) 30 { 31 sum1+=shu[i]; 32 g1++; 33 } 34 else 35 { 36 sum2+=shu[i]; 37 g2++; 38 } 39 } 40 return ; 41 } 42 43 void work() 44 { 45 double a=(100-k)*0.01; 46 ans=(a*sum1 + sum2*1.0)/(g2*1.0 + g1*a); 47 printf("%.9lf\n",ans); 48 } 49 50 int main() 51 { 52 readdate(); 53 work(); 54 return 0; 55 }
其次,我才想到了二分答案。
首先读入每个数,在读的时候要特判,如果读进去的数都是一样的,或者n==1,即只有一个值,那我们就可以直接输出了。
然后读完了所有数后,要进行sort排序,我是按照从大到小排序的,讲二分的 l 设为数列里最小的,r 设为最大的。因为是double型,有精度问题,所以while循环的条件就是(r-l)> 10-12 ,然后就是常规取mid了。这个地方,check的条件也是十分明显的。首先把所有大于mid的数加起来(因为精度问题,也需要用(a-mid)> 10-12 来判断大小),然后每次一个比它小的数,就从sum中减去,如果不够了,就说明我们枚举的ans大了,这时候就r=mid,否则这个方案就是可行的,l=mid(同时我习惯于在这一步就把答案更新为l)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int maxn=10000+10; 9 10 int n,k; 11 double shu[maxn]; 12 double l,r,mid; 13 double ans; 14 bool flag; 15 16 bool cmp(double x,double y) 17 { 18 return x>y; 19 } 20 21 void readdate() 22 { 23 cin>>n>>k; 24 for(int i=1;i<=n;i++) 25 { 26 scanf("%lf",&shu[i]); 27 if(shu[i] != shu[i-1]) 28 flag=true; 29 } 30 if(!flag || n==1) //特判 31 { 32 printf("%.9f\n",shu[1]); 33 exit(0); 34 } 35 sort(shu+1,shu+1+n,cmp); 36 l=shu[n]; 37 r=shu[1]; 38 } 39 40 bool check(double now) 41 { 42 double sum1=0,sum2=0; 43 for(int i=1;i<=n;i++) 44 { 45 if(shu[i]-now>=1e-12) 46 { 47 sum1+=(shu[i]-now); 48 } 49 else 50 { 51 double jian=(now-shu[i])*100.0 / (100.0 - k); //因为有能量损失,所以减掉需要的能量时,要除以能量损失百分比。为了精度,我上下同时乘以了一个100. 52 if(jian-sum1>1e-12) 53 { 54 return false; 55 } 56 else 57 { 58 sum1-=jian; 59 } 60 } 61 } 62 return true; 63 } 64 65 void work() 66 { 67 while(r-l>1e-12) 68 { 69 mid=(r+l)/2.0; 70 if(check(mid)) 71 { 72 ans=mid; 73 l=mid; 74 } 75 else 76 { 77 r=mid; 78 } 79 } 80 printf("%.9f\n",ans); 81 } 82 83 int main() 84 { 85 readdate(); 86 work(); 87 return 0; 88 }
第三道题,很明显的一个最长上升子序列嘛。注意的条件不就是要判断两个相邻值之间项数差值是否大于d,以及注意数据范围,用nlogn的方法求最长上升子序列就可以了。我用的是之前在一道题解里看到过得求上升子序列的方法,正好有一个q的数组可以来判断d的关系。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 using namespace std; 7 8 const int maxn=100000+10; 9 10 int n,d,tot; 11 int q[maxn]; 12 int c[maxn]; 13 int s[maxn]; 14 15 int main() 16 { 17 while(scanf("%d%d",&n,&d)!=EOF) 18 { 19 tot=0; 20 memset(c,0x3f3f3f,sizeof(c)); //初始化为极大值 21 for(int i=1;i<=n;i++) 22 { 23 scanf("%d",&s[i]); 24 } 25 for(int i=1;i<=n;i++) 26 { 27 q[i]=lower_bound(c+1,c+1+n,s[i])-c; //找到第一个大于等于s[i]的数,q存的就是当前第i个数字所存的位置 28 if(q[i]>tot) 29 tot=q[i]; //如果q[i]找到位置都大于答案了,就重新赋值答案tot 30 int cha=i-d; //cha值表示当前这个点最近能够取得它之前的哪个点 31 if(cha>0 && c[q[cha]]>s[cha]) //如果cha>0,即表示i之前有可取的点。同时在答案序列c里,这个可取点所对应的位置的数要大于当前这个点的值,那么我们的重新赋值才有意义,可以为后来的点腾更多的空间 32 c[q[cha]]=s[cha]; //重新赋值q[cha]所对应的数列c的值 33 } 34 cout<<tot<<endl; 35 } 36 return 0; 37 }
至于第四道题嘛,洛谷上的大佬都讲得很清楚了,一个状压dp。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 6 int floors[5200][15]; 7 int f[5200]; 8 int maps[15][15]; 9 int n,m,st,en,va; 10 int ans=0x3f3f3f; 11 12 void clear() 13 { 14 memset(floors,0,sizeof(floors)); 15 memset(f,0x3f3f,sizeof(f)); 16 } 17 18 void work(int k) 19 { 20 int N=1<<n; 21 f[1<<k]=0; 22 floors[(1<<k)][k]=1; 23 for(int i=0;i<N;i++) 24 for(int j=0;j<n;j++) 25 if((i & (1<<j))>0) 26 for(int w=0;w<n;w++) 27 if(((i & (1<<w))==0) && (maps[j][w]!=-1)) 28 { 29 int r=(1<<w); 30 int wi=f[i] + floors[i][j]*maps[j][w]; 31 if(f[i+r] > wi) 32 { 33 f[i+r]=wi; 34 for(int l=0;l<n;l++) 35 floors[i+r][l]=floors[i][l]; 36 floors[i+r][w]=floors[i][j]+1; 37 } 38 } 39 ans=min(ans,f[N-1]); 40 } 41 42 int main() 43 { 44 memset(maps,-1,sizeof(maps)); 45 scanf("%d%d",&n,&m); 46 for(int i=1;i<=m;i++) 47 { 48 scanf("%d%d%d",&st,&en,&va); 49 if(maps[st-1][en-1]>va || maps[st-1][en-1]==-1) 50 maps[st-1][en-1]=maps[en-1][st-1]=va; 51 } 52 for(int i=0;i<n;i++) 53 { 54 clear(); 55 work(i); 56 } 57 cout<<ans<<endl; 58 return 0; 59 }

浙公网安备 33010602011771号