2017年4月3日
cumt2017春季——训练赛(1)
A.HihoCoder 1339 (dp)
思路:
比较清晰呢,就是个dp吧。定义一下状态,dp[i][j]:前i个骰子,扔出点数和为j的方案数。然后不就很好写了嘛递推式,dp[i][j] = dp[i - 1][j - k](1<=k<=6)。
好像题就做完了诶,可是窝wa了两发什么鬼。第一发爆int,第二发爆longlong,这里的trick就是方案数可能非常大,所以这题的应该把状态定义为dp[i][j]:前i个骰子,扔出点数和为j的概率。
递推式大同小异。就完了?好像就这样,不过窝好像发现蒋理文用long double存了方案数也A了。。很骚很刚。
1 #include <bits/stdc++.h> 2 using namespace std; 3 double dp[105][605]; 4 int main() 5 { 6 int n, m; 7 scanf("%d%d", &n, &m); 8 for(int i = 1; i <= 6; i++) dp[1][i] = 1.0 / 6; 9 for(int i = 2; i <= n; i++) 10 { 11 for(int j = i; j <= 6 * n; j++) 12 { 13 for(int k = 1; k <= 6; k ++) 14 { 15 if(j > k) dp[i][j] += dp[i - 1][j - k] / 6.0; 16 } 17 } 18 } 19 printf("%.2f\n", 100.0 * dp[n][m]); 20 21 return 0; 22 }
B.UVA 11389 (贪心)
题意:
有n个上午的任务和下午的任务,分配给司机,如果工作总时间超过d,超过的部分要给加班费;现在让你安排任务,问最小的加班分花费。
思路:
排序一下,首尾加一下就好了。非常简单的贪心昂。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int mod = 1e9 + 7; 4 const int maxn = 100 + 5; 5 const int INF = 0x3f3f3f3f; 6 typedef long long LL; 7 typedef unsigned long long ull; 8 9 int day[maxn], night[maxn]; 10 11 int main() 12 { 13 int n, d, r; 14 while(~scanf("%d%d%d", &n, &d, &r)) 15 { 16 if(!n && !d && !r) break; 17 for(int i = 0; i < n; i++) 18 { 19 scanf("%d", &day[i]); 20 } 21 for(int i = 0; i < n; i++) 22 { 23 scanf("%d", &night[i]); 24 } 25 sort(day, day + n, greater<int>()); 26 sort(night, night + n, less<int>()); 27 28 LL sum = 0; 29 for(int i = 0; i < n; i++) 30 { 31 int temp = day[i] + night[i]; 32 if(temp > d) 33 { 34 sum += (temp - d) * r; 35 } 36 } 37 cout << sum << "\n"; 38 } 39 return 0; 40 }
C.HDU 1969 (二分)
思路:
二分。然后这里有个trick,就是求π的时候,const double pi = acos(-1.0)。这样能够保证精度足够,而不是手写一个,除非你能背的很长很长2333.
然后贴两个代码。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const double pi = acos(-1.0); 4 5 int cake[10000 + 5]; 6 int T, n, f; 7 bool judge(double mid) 8 { 9 int ret = 0; 10 for(int i = 0; i < n; i++) 11 { 12 ret += int(cake[i] * cake[i] / (mid * mid)); 13 } 14 return ret >= f + 1; 15 } 16 17 int main() 18 { 19 scanf("%d", &T); 20 while(T--) 21 { 22 scanf("%d%d", &n, &f); 23 for(int i = 0; i < n; i++) 24 { 25 int x; 26 scanf("%d", &x); 27 cake[i] = x; 28 } 29 30 double lb = 0, rb = 1e5; 31 for(int i =0; i < 100; i++) 32 { 33 double mid = (lb + rb) / 2; 34 if(judge(mid)) lb = mid; 35 else rb = mid; 36 } 37 printf("%.4f\n", pi * lb * lb); 38 } 39 return 0; 40 }
这个直接是二分的最后的结果。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const double pi = acos(-1.0); 4 5 double cake[10000 + 5]; 6 int T, n, f; 7 bool judge(double mid) 8 { 9 int ret = 0; 10 for(int i = 0; i < n; i++) 11 { 12 ret += int(cake[i] / mid); 13 } 14 return ret >= f + 1; 15 } 16 17 int main() 18 { 19 scanf("%d", &T); 20 while(T--) 21 { 22 scanf("%d%d", &n, &f); 23 for(int i = 0; i < n; i++) 24 { 25 int x; 26 scanf("%d", &x); 27 cake[i] = pi * x * x; 28 } 29 30 double lb = 0, rb = pi * 2e4 * 2e4; 31 for(int i =0; i < 100; i++) 32 { 33 double mid = (lb + rb) / 2; 34 if(judge(mid)) lb = mid; 35 else rb = mid; 36 } 37 printf("%.4f\n", lb); 38 } 39 return 0; 40 }
D.UVA 11134 (经典贪心的变形)
题意:
在一个n*n(1<=n<=5000)的棋盘上放置n个车,每个车都只能在给定的一个矩形里放置,使其n个车两两不在同一行和同一列,判断并给出解决方案。
思路:
这个题比较难啊,这题本来打算拿来防AK的,第一次做的话会比较困难。
首先我们思考一个简单的问题,如果把题目里的二维的改成一维的,你会不会做,就是有一行,让你放,每个车可以放一个区间[li,ri],问你能不能找到一种方案,使得每个车不在同一个格子里。
针对这个问题是不是,这样贪心:枚举每个格子,记为i,那么是不是所有满足左端点li <= i的里头,挑一个右端点尽可能小的来放在这一个格子里面?,因为右端点越大,它后面可能可以放的格子越多,越小,可放的格子越小,所以我们这样贪心的来放。
转换!这个问题可以分解成x方向上的问题,和相似的y方向上的问题。然后这个问题就转换成很经典的问题了,记不记得《挑战》奶牛擦防晒霜那题,经典的适配有上下边界的贪心。
1 #include <stdio.h> 2 #include <bits/stdc++.h> 3 using namespace std; 4 const int mod = 1e9 + 7; 5 const int maxn = 5000 + 5; 6 const int INF = 0x3f3f3f3f; 7 typedef long long LL; 8 typedef pair<int, int>pii; 9 typedef pair<LL, LL>pLL; 10 typedef unsigned long long ull; 11 12 struct node 13 { 14 int le, ri, order; 15 bool operator < (const node &other)const 16 { 17 return le < other.le || (le == other.le && ri < other.ri); 18 } 19 }x[maxn], y[maxn]; 20 int ansx[maxn], ansy[maxn]; 21 22 bool solve(int n, node z[], int ans[]) 23 { 24 sort(z, z + n); 25 priority_queue<pii, vector<pii>, greater<pii>>que; 26 int cnt = 0, ret = 0; 27 for(int i = 1; i <= n; i++) 28 { 29 while(cnt < n && z[cnt].le <= i) 30 { 31 que.push({z[cnt].ri, z[cnt].order}); 32 cnt++; 33 } 34 if(que.size() == 0) return false; 35 36 pii p = que.top(); 37 if(p.first < i) return false; 38 ans[p.second] = i; 39 que.pop(); 40 ret++; 41 } 42 return ret == n; 43 } 44 45 int main() 46 { 47 int n; 48 while(~scanf("%d", &n)) 49 { 50 if(n == 0) break; 51 for(int i = 0; i < n; i++) 52 { 53 scanf("%d%d%d%d", &x[i].le, &y[i].le, &x[i].ri, &y[i].ri); 54 x[i].order = y[i].order = i; 55 } 56 if(solve(n, x, ansx) && solve(n, y, ansy)) 57 { 58 for(int i = 0; i < n; i++) 59 { 60 printf("%d %d\n", ansx[i], ansy[i]); 61 } 62 } 63 else puts("IMPOSSIBLE"); 64 } 65 return 0; 66 }
E.CodeForces 779A (water)
题意:
有A组和B组,每组有n个人,A组里面每个人的分值为ai,B组里面每个人的分值为bi。1<=ai,bi<=5,至少双方要交换多少人才能使使A组里面每种分值的人要和B组里面一样多。
思路:
先用一个cnt数组,记录一下每个分数的两组的人数差di。如果人数差di是奇数,false,那怎么也调不平呀对吧,因为3个人怎么也不可能分成两份。剩下的如果能顺利完成交换,是不是sigma{di}(di > 0) 和 sigma{di}(di < 0)要相等才能换成功呀,那是不是就是sigma{(abs(di)}是偶数呀。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int INF = 0x3f3f3f3f; 4 const int maxn = 100 + 5; 5 typedef long long LL; 6 typedef pair<int, int>pii; 7 8 int n; 9 int cnt[10]; 10 int a[105], b[105]; 11 12 int solve() 13 { 14 int ans = 0; 15 for(int i = 1; i <= 5; i++) 16 { 17 if(abs(cnt[i]) % 2 != 0) return -1; 18 ans += abs(cnt[i]) / 2; 19 } 20 if(ans % 2 == 1) return -1; 21 return ans / 2; 22 } 23 24 int main() 25 { 26 cin >> n; 27 for(int i = 0; i < n; i++) cin >> a[i], cnt[a[i]]++; 28 for(int i = 0; i < n; i++) cin >> b[i], cnt[b[i]]--; 29 cout << solve() << endl; 30 return 0; 31 }