2016summer 训练第二场
1.http://acm.hdu.edu.cn/showproblem.php?pid=5112
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> using namespace std; const int MAXN = 100000+10; struct Node{ int t; int x; bool operator<(Node&a){ return t < a.t; } }; Node S[MAXN]; int main(){ int i, T,n; int iCase = 1; scanf("%d", &T); while(T--){ scanf("%d", &n); for (i = 0; i < n; i++) scanf("%d%d", &S[i].t, &S[i].x); sort(S, S + n); double Max = 0; for (i = 0; i < n - 1; i++){ double v = abs(S[i + 1].x - S[i].x)*1.0 / (S[i + 1].t - S[i].t); Max = max(Max, v); } printf("Case #%d: %.2lf\n",iCase++, Max); } return 0; }
2.http://acm.hdu.edu.cn/showproblem.php?pid=5122
一开始一看到以为求逆序来搞,一看题就写了个数状数组,nlogn超时了。当然一定是太性急了,这个题其实就是判断下它后面是否有数字比它小,有的话需要将其往后移一次,否则不变。这样只需要判断下每个数后面是否有有比它小的数,于是将数组倒置,即为判断每个数前面是否有比它大的数,只需要从前向后扫描一遍,需要一个数Min记录当前已扫描结束的序列中最小的数。这样只要碰到后迷死你右数大于这个最小的数就计数一次,否则更新这个Min。
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> using namespace std; const int MAXN = 10000001; int a[MAXN]; int main(){ int i, T, n; int iCase = 1; scanf("%d", &T); while (T--){ scanf("%d", &n); for (i = n; i > 0; i--) scanf("%d", &a[i]); int Min = a[1]; int ans = 0; for (i = 2; i <= n; i++){ if (a[i] > Min) ans++; else Min = a[i]; } printf("Case #%d: %d\n",iCase++,ans); } return 0; }
3.http://acm.hdu.edu.cn/showproblem.php?pid=5120
模板很重要啊。这里主要的是求两个圆相交的面积,有了模板直接搞。最终面积=大圆交大圆-2*大圆交小圆+小圆交小圆。
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cmath> #include<algorithm> using namespace std; const double EPS = 1e-8; const double pi = acos(-1.0); struct Point{ double x, y; Point(double a=0, double b=0) :x(a), y(b){} }; struct Circle{ Circle(Point a, double x) :o(a), r(x){} Point o; double r; }; int RlCmp(double r1, double r2){ if (abs(r1 - r2) < EPS) return 0; return r1 < r2 ? -1 : 1; } Point operator-(Point a, Point b){ return Point(a.x - b.x, a.y - b.y); } Point operator+(Point a, Point b){ return Point(a.x + b.x, a.y + b.y); } double Mold(Point a){ return sqrt(a.x*a.x + a.y*a.y); } double Dis(Point a, Point b){ return Mold(a - b); } //园与圆相交面积模板 double CircleCrossArea(Circle A,Circle B){ double r1 = A.r, r2 = B.r; double d = Dis(A.o, B.o),r=min(r1,r2); if (RlCmp(d, r1 + r2) >= 0) return 0; //相离或者外切 if (RlCmp(d, abs(r1 - r2)) <= 0) return pi*r*r; //内含 //将r1放在圆心 double x1 = (d*d + r1*r1 - r2*r2) / (2 * d); double s1 = x1*sqrt(r1*r1 - x1*x1) - r1*r1*acos(x1/r1); //将r2放在圆心 double x2 = (d*d + r2*r2 - r1*r1) / (2 * d); double s2 = x2*sqrt(r2*r2 - x2*x2) - r2*r2*acos(x2 / r2); return abs(s1 + s2); } int main(){ int T, iCase = 1; Point q,o; double r1, r2; scanf("%d", &T); while (T--){ scanf("%lf%lf",&r1,&r2); scanf("%lf%lf",&q.x,&q.y); scanf("%lf%lf", &o.x,&o.y); Circle A1(q, r1); Circle A2(q, r2); Circle B1(o, r1); Circle B2(o, r2); double a = CircleCrossArea(A2, B2); double b = CircleCrossArea(A2, B1); double c = CircleCrossArea(A1, B1); printf("Case #%d: %.6lf\n", iCase++, a - 2 * b + c); } return 0; }
4.http://acm.hdu.edu.cn/showproblem.php?pid=5113
一个很好的搜索题。对矩阵从左到右,一行一行的进行搜索(不要把回溯写错或者写漏掉.....老毛病).注意剪枝,一个剪枝条件是,当某一种颜色的数量超过(格子总数+1)/2时,剩下颜色拼出的一定是不合法的。
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> using namespace std; const int MAXN = 7; int a[7][7]; int b[MAXN*MAXN]; int k, n, m,iCase; int dir[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; bool ans; bool isLeg(int x, int y,int val){ //判断在a[x][y]处填颜色val是否合法 return x - 1 >=0 && a[x - 1][y] != val &&y - 1 >=0 && a[x][y - 1] != val; } void DFS(int r,int c,int cur){ for (int i = 1; i <= k; i++){ if (b[i]>(cur+1)/2) //剪枝 return; } if (c > m){ c = 1; r++; } if (r > n){ //r>n时,表明已经搜索完成 ans = true; printf("Case #%d:\nYES\n", iCase++); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++){ printf(j == m ? "%d\n" : "%d ", a[i][j]); } } if (ans) return; for (int i = 1; i <= k; i++){ if (b[i] > 0 && isLeg(r, c, i)){ a[r][c] = i; b[i]--; DFS(r, c+1,cur-1); //记得回溯洛...... b[i]++; a[r][c] = 0; } } } int main(){ int T, i,j; scanf("%d", &T); iCase = 1; while (T--){ scanf("%d%d%d", &n, &m,&k); for (i = 1; i <= k; i++) scanf("%d", &b[i]); memset(a, 0, sizeof(a)); ans = false; DFS(1, 1,n*m); if (!ans) printf("Case #%d:\nNO\n", iCase++); } return 0; }
5.http://acm.hdu.edu.cn/showproblem.php?pid=5119
背包问题,真难看出来....。用dp[i][j]表示前i个数中异或值为j的选法数。则得到状态转移方程dp[i][j]=dp[i-1][j]+dp[i-1][j^a[i]]; 这里有一个性质:若a^b=c,则b^c=a,a^c=b;题目还有一个坑点,Mi<=10^6,但是他们的异或值可能会超过10^6,于是数组开在10^6大一点不行,但是开在10^7又会爆掉。考虑到10^6的二进制数异或值不会大于2^20次方,从而可以拿这个数来开数组。另外,第i行只与第i-1行有关,可以使用滚动数组。
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int MAXN = 1<<20; LL n, m; LL a[45]; LL f[2][MAXN]; int main(){ LL i, iCase, T,j; iCase = 1; scanf("%I64d", &T); while (T--){ scanf("%I64d%I64d", &n, &m); for (i = 1; i <= n; i++) scanf("%I64d", &a[i]); memset(f, 0, sizeof(f)); //从前0个数中选取0个数,异或值等于0,方法有一种,就是什么也不选 f[0][0] = 1; for (i = 1; i <= n; i++){ for (j=0;j<MAXN; j++){ f[i%2][j] = f[(i-1)%2][j] + f[(i-1)%2][j^a[i]]; } } LL ans = 1; ans <<= n; for (i =0; i <m;i++) ans-= f[n%2][i]; printf("Case #%I64d: %I64d\n",iCase++,ans); } return 0; }