2016年中国大学生程序设计竞赛(杭州)解题报告
还没做完,ACM的题目真多,一场考试有11道。MD我只有一个人。
A题(HDU5933)
分析:容易发现最左边的只能从它右边得到,容易想到一个贪心过程,只需要没到规定大小就合并,到了就分裂,不难证明这是最优的。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 template<typename T> 5 void in(T &x){ 6 char ch = getchar();x=0; 7 while(ch>'9'||ch<'0') ch = getchar(); 8 while(ch <= '9' && ch >= '0') x = x*10+ch-'0',ch = getchar(); 9 } 10 ll a[120000]; 11 12 int main(){ 13 int t; in(t); 14 for(int ti = 1; ti <= t; ti++) { 15 ll n,k; in(n),in(k); 16 ll sum = 0; 17 for(int i = 1; i <= n; i++){ 18 in(a[i]); 19 sum = sum + a[i]; 20 } 21 printf("Case #%d: ",ti); 22 ll ans = 0; 23 if(sum % k != 0){printf("-1\n");} 24 else{ 25 ll last = a[1]; 26 for(int i = 2; i <= n;){ 27 if(last == sum/k){last = a[i]; i++;continue;} 28 if(last < sum/k){last += a[i]; ans ++;i++;} 29 else{last -= (sum/k);ans++;} 30 } 31 ans += last/(sum/k)-1; 32 printf("%I64d\n",ans); 33 } 34 } 35 return 0; 36 }
B题(HDU5934)
分析:将图画出来容易建模成一张图。不难发现只需要缩点后将入度为0的点全部选起来就可以了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int x[1200],y[1200],r[1200],c[1200],num; 4 int arr[1200],scc[1200],dfn[1200],low[1200]; 5 int minn[1200],cl; 6 vector <int> g[1200]; 7 void tarjan(int); 8 const double eps = 1e-6; 9 10 int main(){ 11 int t; 12 scanf("%d",&t); 13 for(int o = 1 ;o <= t ; o++){ 14 int n; 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++) g[i].clear(); 17 for(int i=1;i<=n;i++){ 18 scanf("%d%d%d%d",&x[i],&y[i],&r[i],&c[i]); 19 } 20 for(int i=1;i<=n;i++){ 21 for(int j=1;j<=n;j++){ 22 if(i == j) continue; 23 double dist = sqrt(pow(x[j]-x[i],2)+ pow(y[j]-y[i],2)); 24 if((double)r[i]-dist>=-eps){ 25 g[i].push_back(j); 26 } 27 } 28 } 29 memset(arr,0,sizeof(arr)); 30 memset(dfn,0,sizeof(dfn)); 31 memset(low,0,sizeof(low)); 32 memset(scc,0,sizeof(scc)); 33 cl = 0,num = 0; 34 memset(minn,127/3,sizeof(minn)); 35 for(int i=1;i<=n;i++){ 36 if(arr[i]) continue; 37 tarjan(i); 38 } 39 memset(arr,0,sizeof(arr)); 40 for(int i=1;i<=n;i++){ 41 for(int j=0;j<g[i].size();j++){ 42 if(scc[g[i][j]]==scc[i])continue; 43 arr[scc[g[i][j]]] = 1; 44 } 45 } 46 int ans = 0; 47 for(int i=1;i<=num;i++) 48 if(!arr[i]) 49 ans+=minn[i]; 50 printf("Case #%d: %d\n",o,ans); 51 } 52 return 0; 53 } 54 55 stack <int> sta; 56 void tarjan(int now){ 57 low[now] = dfn[now] = ++cl; 58 sta.push(now); 59 for(int i=0;i<g[now].size();i++){ 60 int k = g[now][i]; 61 if(arr[k]) continue; 62 if(dfn[k]){ 63 low[now] = min(low[now],dfn[k]); 64 }else{ 65 tarjan(k); 66 low[now] = min(low[now],low[k]); 67 } 68 } 69 if(low[now] == dfn[now]){ 70 num++; 71 while(!sta.empty()){ 72 int k = sta.top(); 73 sta.pop(); 74 scc[k] = num; 75 arr[k] = 1; 76 minn[num] = min(minn[num],c[k]); 77 if(k == now)break; 78 } 79 } 80 }
C题(HDU5935)
分析:容易想到一种贪心策略。只需要从后往前在尽量短的时间内跑完这一段。那么可以用二分法。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int a[120000]; 4 double v; 5 int last,now; 6 long long divide(long long l,long long r){ 7 if(l == r) return l; 8 long long mid = (l+r) >> 1; 9 if((double)(last-now)/(double)mid <=v){ 10 return divide(l,mid); 11 }else 12 return divide(mid+1,r); 13 } 14 15 int main(){ 16 int t,ti = 1; scanf("%d",&t); 17 while(t--){ 18 memset(a,0,sizeof(a)); 19 int n; scanf("%d",&n); 20 for(int i=1;i<=n;i++) 21 scanf("%d",&a[i]); 22 v = a[n]-a[n-1]; 23 long long ans = 1; 24 for(int i=n-2;i>=0;i--){ 25 last = a[i+1];now = a[i]; 26 long long k = divide(1,LONG_LONG_MAX/2); 27 ans += k; 28 v = (double)(last-a[i])/(double)k; 29 } 30 printf("Case #%d: %I64d\n",ti,ans);//I64d 31 ti++; 32 } 33 }
D题以后待填