Topcoder SRM 608 div1 题解
Easy(300pts):
题目大意:有n个盒子,一共有S个苹果,每个盒子有多少个苹果不知道,但是知道每个盒子的苹果下限和上限。现在要至少选择X个苹果,问如果要保证无论如何都能获得至少X个苹果,至少需要选择多少个盒子。数据满足n<=50。
首先第一个方面,如果我们选择的盒子的下限大于等于X,那么显然我们一定可以获得那么多的苹果,
所以我们将盒子的下限排序,然后从大到小累加即可。
另一方面,如果我们没有选择的盒子的上限小于等于S-X,那么显然我们也一定可以获得那么多的苹果,
于是我们再按照上限排序,然后扫一遍累加即可。
时间复杂度O(nlogn),代码如下:
1 #include <bits/stdc++.h> 2 #define Maxn 57 3 using namespace std; 4 int n; 5 int l[Maxn],r[Maxn]; 6 class MysticAndCandies 7 { 8 public: 9 int minBoxes(int C, int X, vector <int> low, vector <int> high) 10 { 11 n=low.size(); 12 for (int i=1;i<=n;i++) l[i]=low[i-1]; 13 for (int i=1;i<=n;i++) r[i]=high[i-1]; 14 sort(l+1,l+n+1); 15 sort(r+1,r+n+1); 16 for (int i=1;i<n+1-i;i++) swap(l[i],l[n+1-i]),swap(r[i],r[n+1-i]); 17 int ans,sum1=0,sum2=0; 18 for (int i=1;i<=n;i++) sum2+=r[i]; 19 for (ans=1;ans<=n;ans++) 20 { 21 sum1+=l[ans]; 22 sum2-=r[ans]; 23 if (sum1>=X||C-sum2>=X) break; 24 } 25 return ans; 26 } 27 };
Medium(600pts):
题目大意:给出一个n个点的有向图,假设长度为L的路径一共有S条(路径可以经过重复的点多次,S也可以是无穷大),问当L非常大的时候,S的量级是L的几次方。当然,有可能S不能用L的几次方表示,那么输出-1。数据满足n<=50。
通过题面来看,就感觉这题很厉害。。。
我们先从一个环来考虑,一个环的长度为L的路径显然只有有数条,那么这个时候S就是L的一次方量级。
然后我们来考虑一个强联通分量,如果这个强联通分量不仅仅只是一个环,那么路径条数就是指数级别的了。
回到原题,我们把原图中的强联通分量全部求出来,
至少图变成了一个DAG,然后发现所有的SCC必须是环,
然后差不多dp之类的都可以做了。
由于这道题n<=50,所以根本不需要什么Tarjan,直接floyed就行了。
时间复杂度O(n^3),代码如下:
1 #include <bits/stdc++.h> 2 #define Maxn 57 3 using namespace std; 4 bool vis[Maxn][Maxn]; 5 int fa[Maxn],sum[Maxn],sumedge[Maxn]; 6 int r[Maxn],f[Maxn]; 7 int n; 8 class BigO 9 { 10 public: 11 int minK(vector <string> graph) 12 { 13 n=graph.size(); 14 for (int i=0;i<n;i++) 15 for (int j=0;j<n;j++) 16 if (graph[i][j]=='Y') vis[i][j]=true; else vis[i][j]=false; 17 for (int i=0;i<n;i++) 18 vis[i][i]=true; 19 for (int k=0;k<n;k++) 20 for (int i=0;i<n;i++) 21 for (int j=0;j<n;j++) 22 if (vis[i][k]&&vis[k][j]) vis[i][j]=true; 23 memset(sum,0,sizeof(sum)); 24 for (int i=0;i<n;i++) 25 for (int j=0;j<n;j++) 26 if (vis[i][j]&&vis[j][i]) 27 { 28 fa[i]=j; 29 ++sum[j]; 30 break; 31 } 32 memset(sumedge,0,sizeof(sumedge)); 33 for (int i=0;i<n;i++) 34 for (int j=0;j<n;j++) 35 if (graph[i][j]=='Y'&&fa[i]==fa[j]&&i!=j) 36 ++sumedge[fa[i]]; 37 memset(r,0,sizeof(r)); 38 memset(f,0,sizeof(f)); 39 for (int i=0;i<n;i++) 40 { 41 //the SCC is more than a circle 42 if (sumedge[i]>sum[i]) return -1; 43 //the SCC is more than a point 44 if (sumedge[i]!=0) r[i]=1; 45 f[i]=r[i]; 46 } 47 for (int T=0;T<n;T++) 48 for (int i=0;i<n;i++) 49 for (int j=0;j<n;j++) 50 if (fa[i]!=fa[j]&&graph[i][j]=='Y') 51 f[fa[j]]=max(f[fa[j]],f[fa[i]]+r[fa[j]]); 52 int res=0; 53 for (int i=0;i<n;i++) 54 res=max(f[i]-1,res); 55 return res; 56 } 57 };
Hard(900pts):
题目大意:有一个机器人在x轴上,初始位置为0,它接受到了一系列指令,每次向左一个单位或者向右一个单位。如果现在在左边界上,那么就不能再往左了,向左的指令会被忽略;如果现在在右边界上,那么就不能再往右了,向右的指令会被忽略。现在对于minA<=A<=maxA,minB<=B<=maxB,求出以A为左边界,B为右边界的最终位置的总和,数据满足所有数<=5000。
这题是个神结论题,不打算多说。。。
我们假设边界都是无穷大的时候,求出出现过的最左侧位置和最右侧位置。
如果A和B分别大于等于两边边界,那么答案就是最终位置。
如果不是的话,有一个结论就是(a,b)等于(a-1,b+1)-1,然后按照差暴力就可以了。
时间复杂度O(nX+X^2),代码如下:
1 #include <bits/stdc++.h> 2 #define Maxa 5007 3 using namespace std; 4 int ans[Maxa][Maxa]; 5 string s; 6 long long res=0; 7 class OneDimensionalRobot 8 { 9 public: 10 long long theSum(vector <string> commands1, vector <string> commands2, int minA, int maxA, int minB, int maxB) 11 { 12 s.clear(); 13 for (int i=0;i<commands1.size();i++) s+=commands1[i]; 14 for (int i=0;i<commands2.size();i++) s+=commands2[i]; 15 int left=0,right=0,pos=0; 16 for (int i=0;i<s.length();i++) 17 { 18 if (s[i]=='R') ++pos; else --pos; 19 if (-pos>left) left=-pos; 20 if (pos>right) right=pos; 21 } 22 memset(ans,0,sizeof(ans)); 23 for (int i=minA;i<=maxA;i++) 24 for (int j=minB;j<=maxB;j++) 25 { 26 if (i>=left&&j>=right) ans[i][j]=pos; else 27 if (i==minA||j==maxB) 28 { 29 int now=0; 30 for (int k=0;k<s.length();k++) 31 { 32 if (s[k]=='R') ++now; else --now; 33 if (-now>i) now=-i; 34 if (now>j) now=j; 35 } 36 ans[i][j]=now; 37 } else 38 { 39 ans[i][j]=ans[i-1][j+1]; 40 if (left>i-1||right>j) --ans[i][j]; 41 } 42 } 43 for (int i=minA;i<=maxA;i++) 44 for (int j=minB;j<=maxB;j++) 45 res+=ans[i][j]; 46 return res; 47 } 48 };