2014ACM/ICPC亚洲区北京站 上交命题
A http://acm.hdu.edu.cn/showproblem.php?pid=5112
输入n个时刻和位置,问那两个时刻间速度最快。
解法:按照时间排序,然后依次求相邻两个之间的速度,速度=ds/dt
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int M=1e4+10; 5 struct G{ 6 int t,x; 7 friend bool operator <(const G &a,const G &b){ 8 return a.t<b.t; 9 } 10 }g[M]; 11 int main(){ 12 int t,n; 13 while(~scanf("%d",&t)){ 14 int cas=1; 15 while(t--){ 16 scanf("%d",&n); 17 for(int i=0;i<n;i++){ 18 scanf("%d%d",&g[i].t,&g[i].x); 19 } 20 sort(g,g+n); 21 double ans=0; 22 for(int i=0;i<n-1;i++){ 23 ans=max(ans,abs(g[i].x-g[i+1].x)*1.0/(g[i+1].t-g[i].t)); 24 } 25 printf("Case #%d: %.2f\n",cas++,ans); 26 } 27 } 28 return 0; 29 }
D http://acm.hdu.edu.cn/showproblem.php?pid=5115
有n只狼排成一排,每一步消灭一只,每次消灭花费当前这只狼的a,以及左右两边最近的两只的b,ai+bleft+bright。问消灭所有狼最小花费。
解法:若暴力,可以n!,算花费取最小值。优化一点状态压缩,可以从二进制111-》000,最后dp【0】的最小值就是答案。复杂度2^n.再优化
定义dp【i】【j】表示消灭i到j最小花费。答案就是dp【1】【n】。转移时枚举i到j中最后一个被杀的,假设是k,那么转移就是
dp【i】【j】=min(dp【i】【j】,dp【i】【k-1】+dp【k+1】【j】+a【k】+b【i-1】+b【j+1】);
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int M=256; 5 const int inf=0x3f3f3f3f; 6 int a[M],b[M],dp[M][M]; 7 int main(){ 8 int t,n; 9 while(~scanf("%d",&t)){ 10 int cas=1; 11 while(t--){ 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++){ 14 scanf("%d",&a[i]); 15 } 16 for(int i=1;i<=n;i++){ 17 scanf("%d",&b[i]); 18 } 19 b[0]=b[n+1]=0; 20 for(int i=1;i<=n;i++){ 21 for(int j=i;j<=n;j++){ 22 dp[i][j]=inf; 23 } 24 } 25 for(int len=1;len<=n;len++){ 26 for(int i=1;i+len-1<=n;i++){ 27 for(int j=i;j<i+len;j++){ 28 int cost=a[j]+b[i-1]+b[i+len]; 29 if(j>i){ 30 cost+=dp[i][j-1]; 31 } 32 if(j<i+len-1){ 33 cost+=dp[j+1][i+len-1]; 34 } 35 dp[i][i+len-1]=min(dp[i][i+len-1],cost); 36 } 37 } 38 } 39 printf("Case #%d: %d\n",cas++,dp[1][n]); 40 } 41 } 42 return 0; 43 }
H http://acm.hdu.edu.cn/showproblem.php?pid=5119
有40个数,问选一个子集异或之和大于等于m的有多少种选法。
解法:朴素算法,暴力所有子集验证答案,2^40的时间复杂度,01背包
定义dp【i】【j】表示前n个异或和为j的情况数,则答案为dp【n】【j】,j>=m。
转移就两个 ,选或者不选。
1 #include<cstdio> 2 typedef long long LL; 3 const int M=50; 4 int a[M]; 5 LL dp[M][1<<20]; 6 int main(){ 7 int t,n,m; 8 while(~scanf("%d",&t)){ 9 int cas=1; 10 while(t--){ 11 scanf("%d%d",&n,&m); 12 for(int i=1;i<=n;i++){ 13 scanf("%d",&a[i]); 14 } 15 int big=1<<20; 16 for(int i=0;i<=n;i++){ 17 for(int j=0;j<big;j++){ 18 dp[i][j]=0; 19 } 20 } 21 dp[0][0]=1; 22 for(int i=0;i<=n;i++){ 23 for(int j=0;j<big;j++){ 24 if(!dp[i][j]) continue; 25 dp[i+1][j^a[i+1]]+=dp[i][j]; 26 dp[i+1][j]+=dp[i][j]; 27 } 28 } 29 LL ans=0; 30 for(int j=m;j<big;j++){ 31 ans+=dp[n][j]; 32 } 33 printf("Case #%d: %I64d\n",cas++,ans); 34 } 35 } 36 return 0; 37 }
I http://acm.hdu.edu.cn/showproblem.php?pid=5120
问两个圆环交的面积。
解法:容斥原理?算两个大圆面积交,扣掉左边大圆与右边小圆的交,再扣掉左边小圆与右边大圆的交,再把两个小圆的交加回来。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 const double eps=1e-8; 6 const double pi=acos(-1.0); 7 struct point { 8 double x,y; 9 } c1,c2; 10 double Distance(point p1,point p2) { 11 return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); 12 } 13 point intersection(point u1,point u2,point v1,point v2) { 14 point ret=u1; 15 double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x)) /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x)); 16 ret.x+=(u2.x-u1.x)*t; 17 ret.y+=(u2.y-u1.y)*t; 18 return ret; 19 } 20 void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2) { 21 point p=c; 22 double t; 23 p.x+=l1.y-l2.y; 24 p.y+=l2.x-l1.x; 25 p=intersection(p,c,l1,l2); 26 t=sqrt(r*r-Distance(p,c)*Distance(p,c))/Distance(l1,l2); 27 p1.x=p.x+(l2.x-l1.x)*t; 28 p1.y=p.y+(l2.y-l1.y)*t; 29 p2.x=p.x-(l2.x-l1.x)*t; 30 p2.y=p.y-(l2.y-l1.y)*t; 31 } 32 void intersection_circle_circle(point c1,double r1,point c2,double r2,point& p1,point& p2) { 33 point u,v; 34 double t; 35 t=(1+(r1*r1-r2*r2)/Distance(c1,c2)/Distance(c1,c2))/2; 36 u.x=c1.x+(c2.x-c1.x)*t; 37 u.y=c1.y+(c2.y-c1.y)*t; 38 v.x=u.x+c1.y-c2.y; 39 v.y=u.y-c1.x+c2.x; 40 intersection_line_circle(c1,r1,u,v,p1,p2); 41 } 42 double xmult(point p1,point p2,point p0) { 43 return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); 44 } 45 double area_triangle(point p1,point p2,point p3) { 46 return fabs(xmult(p1,p2,p3))/2; 47 } 48 int same_side(point p1,point p2,point l1,point l2) { 49 return xmult(l1,p1,l2)*xmult(l1,p2,l2)>eps; 50 } 51 double area_circle_circle(point a,double r1,point b,double r2) { 52 if(r1<eps||r2<eps)return 0.0; 53 double temp=Distance(a,b); 54 point p1,p2; 55 double co1,co2,ret=0.0,du1,du2,z; 56 if(temp+eps>=r1+r2) { 57 return 0.0; 58 } else if(temp-eps<=fabs(r1-r2)) { 59 temp=min(r1,r2); 60 return pi*temp*temp; 61 } else { 62 intersection_circle_circle(a,r1,b,r2,p1,p2); 63 temp=Distance(p1,p2); 64 co1=(r1*r1*2.0-temp*temp)/(2.0*r1*r1); 65 du1=acos(co1); 66 z=(r1*r1*du1/2.0-area_triangle(p1,p2,a)); 67 if(same_side(a,b,p1,p2)&&r1<r2)z=pi*r1*r1-z; 68 ret+=z; 69 co2=(r2*r2*2.0-temp*temp)/(2.0*r2*r2); 70 du2=acos(co2); 71 z=(r2*r2*du2/2.0-area_triangle(p1,p2,b)); 72 if(same_side(a,b,p1,p2)&&r2<r1)z=pi*r2*r2-z; 73 ret+=z; 74 return ret; 75 } 76 } 77 int main() { 78 int t; 79 double r,R; 80 while(~scanf("%d",&t)) { 81 int cas=1; 82 while(t--) { 83 scanf("%lf%lf%lf%lf%lf%lf",&r,&R,&c1.x,&c1.y,&c2.x,&c2.y); 84 printf("Case #%d: %.6f\n",cas++,area_circle_circle(c1,R,c2,R)-area_circle_circle(c1,r,c2,R)-area_circle_circle(c1,R,c2,r)+area_circle_circle(c1,r,c2,r)); 85 } 86 } 87 return 0; 88 }
K http://acm.hdu.edu.cn/showproblem.php?pid=5122
输入n的一个排列,问最少多少次操作能将其从小到大排好序, 每次操作可以任意选一个数,不断的往后交换直至后一个大于他停止。
解法:首先选必须要从大往小选,这样最多n次就能排好序,就是逆序的情况。因为如果选个中间的,他往后换的过程可能会遇到一个较大的使得他停下,没达到需要到的位置,从大到小选就避免了这种情况,是最优的方法。问题转化为如何统计步数。我们定义一个需求need,表示当前需要选的数,定义一个指针p,表示当前位置,指针从最后一个位置开始,需求从n开始。每次迭代,若指针的位置的值a【p】恰好等于need,则我们不需要浪费步数,数就在他应该在的位置。若a【p】小于need,说明这个位置应该放的是need,我们需要消耗一步,将前面的need移动至此,但我们不需改动数组的值,因为改动的复杂度是on的。此时我们只需要记录ans++,need--,p不动。若a【p】大于need,说明这个值已经处理过了,p--。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int M=1e6+10; 5 int a[M]; 6 int main(){ 7 int t,n; 8 while(~scanf("%d",&t)){ 9 int cas=1; 10 while(t--){ 11 scanf("%d",&n); 12 for(int i=0;i<n;i++){ 13 scanf("%d",&a[i]); 14 } 15 int ans=0; 16 int p=n-1; 17 for(int i=n;i>=1;i--){ 18 if(a[p]==i){ 19 p--; 20 continue; 21 } 22 if(a[p]>i){ 23 p--; 24 i++; 25 continue; 26 } 27 ans++; 28 } 29 printf("Case #%d: %d\n",cas++,ans); 30 } 31 } 32 return 0; 33 }
end