Topcoder SRM 604 div1题解
CTSC考完跑了过来日常TC~~~
Easy(250pts):
题目大意:有个机器人,一开始的位置在(0,0),第k个回合可以向四个方向移动3^k的距离(不能不动),问是否可以到达(x,y),数据满足|x|,|y|<=10^9。
这题还是很简单的嘛,口亨~~~
首先我们只考虑一个方向,于是发现,每一次可以移动3^k的距离或者不动,于是我们发现这样的序列是有且仅有一个的,
于是我们分开考虑x和y,把两个序列全部预处理出来,
然后直接扫一遍就做完了,
时间复杂度O(log|x|)左右吧,代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 class PowerOfThree 4 { 5 public: 6 string ableToGet(int x, int y) 7 { 8 x=abs(x),y=abs(y); 9 bool check=true; 10 while (x!=0||y!=0) 11 { 12 if ((x%3==0)+(y%3==0)!=1) check=false; 13 if (x%3==2) x=(x+1)/3; else x=x/3; 14 if (y%3==2) y=(y+1)/3; else y=y/3; 15 } 16 if (check) return "Possible"; else return "Impossible"; 17 } 18 };
Medium(550pts):
题目大意:有一棵n个节点的树,有若干节点上有一只狐狸,其它节点没有,所有边的距离为1。现在狐狸要聚集在一起,使得它们形成了一棵树,且每个节点依然最多只有一只狐狸,且移动距离之和最小,输出这个最小值。数据满足n<=50。
我做过的最难的medium吧。。。这题做了我好久好久。。。
首先,最后形成的狐狸一定是一棵树,我们枚举每一个节点i,设i是最后这棵树的root,
我们考虑树形dp,
设f[i][j]表示i这个节点形成的子树中一共放置了j个狐狸,且i这个节点一定有狐狸,且这j个狐狸联通的最优值。
我们考虑如何转移,
假设cur[j]就是当前u这个节点已经处理了若干个儿子的f[u]数组,
于是我们能够得到f[u][i+j]就是从已经处理的若干个儿子中提取i个,从当前正在处理的v节点中提取j个的最优值,
那么已经处理完的若干个儿子对答案的贡献度是cur[i],当前处理的v节点对答案的贡献度是f[v][j]+(u和v这条边对答案的贡献度),
所以f[u][i+j]=cur[i]+f[v][j]+(u和v这条边对答案的贡献度),
所以我们只需要考虑这条边的贡献度就可以了,我们假设size[u]表示u这个节点为根的子树下原始状态下有多少个狐狸,
如果size[v]=j,那么这条边的贡献度就是0;
如果size[v]>j,那么一定有size[v]-j个狐狸从v这个子树出来,那么一定经过这条边,所以这条边贡献度就是size[v]-j;
如果size[v]<j,那么一定有j-size[v]个狐狸进入v这个子树中,它们也一定经过这条边,所以这条边的贡献度就是j-size[v]。
所以无论如何,这条边的贡献度一定是abs(size[v]-j)。
所以我们有f[u][i+j]=cur[i]+f[v][j]+abs(size[v]-j)。
然后边界细节注意一下就可以了,时间复杂度O(n^4),代码如下:
1 #include <bits/stdc++.h> 2 #define Maxn 1007 3 #define inf 1000000007 4 using namespace std; 5 int n,cnt,ans=inf,m; 6 int last[Maxn],other[Maxn],pre[Maxn],sum[Maxn],size[Maxn]; 7 int f[2*Maxn][2*Maxn]; 8 int temp[4*Maxn]; 9 bool fox[Maxn]; 10 class FoxConnection 11 { 12 void insert(int u, int v) 13 { 14 other[++cnt]=v,pre[cnt]=last[u],last[u]=cnt; 15 } 16 void dfs(int u, int fa) 17 { 18 size[u]=fox[u]; 19 int cur[4*Maxn]; 20 memset(cur,0,sizeof(cur)); 21 for (int q=last[u];q;q=pre[q]) 22 { 23 int v=other[q]; 24 if (v!=fa) 25 { 26 dfs(v,u); 27 for (int i=0;i<=m;i++) temp[i]=inf; 28 for (int i=0;i<=m;i++) 29 for (int j=0;j<=m-i;j++) 30 temp[i+j]=min(temp[i+j],cur[i]+f[v][j]+abs(size[v]-j)); 31 for (int i=0;i<=m;i++) cur[i]=temp[i]; 32 size[u]+=size[v]; 33 } 34 } 35 f[u][0]=cur[0]; 36 for (int i=1;i<=m;i++) f[u][i]=cur[i-1]; 37 } 38 int solve(int rt) 39 { 40 for (int i=1;i<=n;i++) 41 for (int j=0;j<=2*n;j++) 42 f[i][j]=inf; 43 dfs(rt,-1); 44 return f[rt][m]; 45 } 46 public: 47 int minimalDistance(vector <int> A, vector <int> B, string haveFox) 48 { 49 n=A.size()+1; 50 for (int i=0;i<n;i++) fox[i+1]=(haveFox[i]=='Y'); 51 m=0; 52 for (int i=1;i<=n;i++) if (fox[i]) ++m; 53 for (int i=0;i<n-1;i++) 54 insert(A[i],B[i]),insert(B[i],A[i]); 55 for (int i=1;i<=n;i++) 56 ans=min(ans,solve(i)); 57 return ans; 58 } 59 };
Hard(1000pts):
这个计算几何题怎么比medium思路简单多了呢。。。
题目大意:给了你n条线段,它们可能有交点,可能有重合,现在把它们视为一个模块,有一张10^9*10^9的长方形纸片,现在复制若干遍这个模块,要求任意两个模块不能相交,要求判断是否可以复制无穷多份。数据满足n<=50。
这题的思路还是很简单的吧,如果能够复制无数份,那么一定是能够往某个方向移动了很小很小的距离,
我们先把所有直线两两处理,
如果重合,直接不用管;
如果没有交点,直接不用管;
如果有一个交点,而且交点不是直线的端点,直接有穷个返回答案;
如果有一个交点,而且交点是直线的端点,那么那个方向一定有角度限制,预处理出角度限制。
所有直线两两处理完了之后,把所有角度限制从小到大扫一遍,然后判定一下就做完了。
时间复杂度O(n^2),代码如下:
1 #include <bits/stdc++.h> 2 #define eps 1e-9 3 #define Maxn 107 4 using namespace std; 5 int n,cnt=0; 6 struct point {double x,y;}; 7 struct line {point a,b;}; 8 line a[Maxn]; 9 pair<double,double> cover[Maxn*Maxn*2]; 10 class FamilyCrest 11 { 12 double cross(double x1 ,double y1, double x2, double y2) 13 { 14 return (x1*y2-x2*y1); 15 } 16 bool samepoint(point a, point b) 17 { 18 //check if point a and point b are the same point 19 if (fabs(a.x-b.x)<eps&&fabs(a.y-b.y)<eps) return true; 20 return false; 21 } 22 bool sameline(line a, line b) 23 { 24 //check if line a and line b are the same line 25 double res=cross(a.a.x-a.b.x,a.a.y-a.b.y,b.a.x-b.b.x,b.a.y-b.b.y); 26 if (fabs(res)<eps) return true; 27 return false; 28 } 29 double sameposition(line a, line b) 30 { 31 //check if the whole segment b is on the same direction of line a 32 //answer>0 means segment b is on the same direction of line a 33 //answer<0 means segment b has a same point with line a 34 //answer=0 means segment b has an end point on the line a 35 double res,res1,res2; 36 res1=cross(a.b.x-a.a.x,a.b.y-a.a.y,b.a.x-a.a.x,b.a.y-a.a.y); 37 res2=cross(a.b.x-a.a.x,a.b.y-a.a.y,b.b.x-a.a.x,b.b.y-a.a.y); 38 res=res1*res2; 39 return res; 40 } 41 void updata(double l, double r) 42 { 43 if (l<r) 44 { 45 cover[++cnt].first=l,cover[cnt].second=r; 46 } else 47 { 48 cover[++cnt].first=l,cover[cnt].second=M_PI; 49 cover[++cnt].first=-M_PI,cover[cnt].second=r; 50 } 51 } 52 bool calc(line a, line b) 53 { 54 //segment a and segment b are on the same line 55 if (sameline(a,b)) return true; 56 //segment a and segment b don't have a same point 57 if (sameposition(a,b)>eps) return true; 58 if (sameposition(b,a)>eps) return true; 59 //segment a and segment b have a same point which is not an end point 60 if (sameposition(a,b)<-eps) return false; 61 if (sameposition(b,a)<-eps) return false; 62 //segment a and segment b have a same point which is an end point 63 point O,A,B; 64 //point O is the end point 65 if (samepoint(a.a,b.a)) O=a.a,A=a.b,B=b.b; else 66 if (samepoint(a.a,b.b)) O=a.a,A=a.b,B=b.a; else 67 if (samepoint(a.b,b.a)) O=a.b,A=a.a,B=b.b; else 68 if (samepoint(a.b,b.b)) O=a.b,A=a.a,B=b.a; 69 if (cross(A.x-O.x,B.x-O.x,A.y-O.y,B.y-O.y)>eps) swap(A,B); 70 updata(atan2(A.y-O.y,A.x-O.x),atan2(O.y-B.y,O.x-B.x)); 71 updata(atan2(O.y-A.y,O.x-A.x),atan2(B.y-O.y,B.x-O.x)); 72 return true; 73 } 74 public: 75 string canBeInfinite(vector <int> A, vector <int> B, vector <int> C, vector <int> D) 76 { 77 n=A.size(); 78 for (int i=1;i<=n;i++) 79 { 80 a[i].a.x=A[i-1]; 81 a[i].a.y=B[i-1]; 82 a[i].b.x=C[i-1]; 83 a[i].b.y=D[i-1]; 84 } 85 for (int i=1;i<=n;i++) 86 for (int j=i+1;j<=n;j++) 87 if (!calc(a[i],a[j])) return "Finite"; 88 sort(cover+1,cover+cnt+1); 89 if (cover[1].first+M_PI>eps) return "Infinite"; 90 if (cover[cnt].second-M_PI<-eps) return "Infinite"; 91 double now=cover[1].second; 92 for (int i=2;i<=cnt;i++) 93 { 94 if (cover[i].first-now>eps) return "Infinite"; 95 now=cover[i].second; 96 } 97 return "Finite"; 98 } 99 };
完结撒花~