2013-5-7 训练赛总结
题目来源 The 13th Zhejiang University Programming Contest
链接: http://openoj.awaysoft.com:8080/judge/contest/view.action?cid=421#overview
A - Alien's Organ
题意: 事件出现平均概率为a, 问出现次数小于等于N的概率
解法: 泊松分布, ,跟着公式算就好.
// p(k) = e^(-ave) * ave^k / k! #include<cstdio> #include<cmath> int main(){ double ave; int T, n; scanf("%d", &T); while( T-- ){ scanf("%d %lf", &n,&ave); double p = 0, a = 1, b = 1; for(int i = 0; i <= n; i++){ p += exp(-ave)*a/b; a *= ave; b *= (i+1); } printf("%.3lf\n", p ); } return 0; }
B - Bad-written Number
题意: LED灯表示一个数字,用了3*3行, 现在有N个数字, 前一个数字的第三列与后一个数字的第一列重叠,问可能出现合法的方案有多少.
解法: 状态压缩DP, dp( i, j ), 表示 前i个数, 最后一个数的第三列状态j, 合法的方案数量. 转移方程为:
dp( i+1, a[k][2] ) += dp( i, j ), 其中 a[k][ 0,1,2 ] ,表示数字 k,的第1,2,3列状态.
#include<cstdlib> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<map> using namespace std; typedef long long LL; const int N = 20100; const LL mod = (LL)1e9+7; char str[3][N]; const char* tar[] = { " _ | ||_|", " | |", " _ _||_ ", " _ _| _|", " |_| |", " _ |_ _|", " _ |_ |_|", " _ | |", " _ |_||_|", " _ |_| _|" }; int a[10][3], b[N], n; LL dp[N][4]; void pre(){ memset( a, 0, sizeof(a) ); for(int i = 0; i <= 9; i++) for(int j = 0; j < 3; j++) for(int k = 0; k < 3; k++) //a[i][j] |= (tar[i][j+3*k] != ' ') << k; a[i][j] = (a[i][j]<<1) | (tar[i][j+3*k] != ' '); //for(int i = 0; i < 10; i++) // printf("%d: %d, %d, %d\n", i, a[i][0], a[i][1], a[i][2] ); } void init(){ memset( str, 0, sizeof(str)); memset( b, 0 , sizeof(b)); for(int i = 0; i < 3; i++){ gets( str[i] ); for(int j = strlen(str[i]); j < 2*n+1; j++) str[i][j] = ' '; } for(int j = 0; j < 3; j++) for(int i = 0; i < 2*n+1; i++) //b[i] |= (str[j][i] != ' ') << j; b[i] = (b[i]<<1) | (str[j][i]!=' '); } void solve(){ memset( dp, 0, sizeof(dp)); dp[0][0] = 1; for(int i = 0; i < n; i++){ int idx = 2*i; for(int j = 0; j < 4; j++){ if( dp[i][j] == 0 ) continue; for(int k = 0; k < 10; k++){ if( (j | a[k][0]) != b[idx] ) continue; if( a[k][1] != b[idx+1] ) continue; if( (a[k][2] & b[idx+2]) != a[k][2] ) continue; dp[i+1][ a[k][2] ] += dp[i][j]; dp[i+1][ a[k][2] ] %= mod; } } } printf("%lld\n", dp[n][ b[2*n] ]); } int main(){ freopen("2.in","r",stdin); pre(); int T; scanf("%d", &T); while( T-- ){ scanf("%d", &n); getchar(); init(); solve(); } return 0; }
C - Carrot Fantasy
题意: 很复杂- - ...和塔防一样.的模拟题.
解法: 按照时间模拟..不过好恶心.
D - Dakar Rally
题意: N段路, 每段路有个长度Li, 与单位耗油量ai,路段开始有个加油站,加油单价pi, 问按顺序走完所有路,最小花费.
解法: 贪心, 经过每一个加油站, 把油全部加满, 并利用单调队列保存, 这个油的数量与单价, 加油时,替换掉贵的, 使用时,从价格低的开始使用.
计算花费, 只算真正使用过的油.
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int N = (int)1e5+100; int n; LL k; struct TMP{ LL a, b, c; void input(){ scanf("%lld%lld%lld",&a,&b,&c); } }route[N]; struct node{ LL gap, c; }Q[N<<1], pre; bool init(){ for(int i = 0; i < n; i++) route[i].input(); for(int i = 0; i < n; i++) if( 1LL*route[i].a*route[i].b > k ) return false; return true; } void solve(){ LL res = 0, s; int l = 0, r = 0; for(int i = 0; i < n; i++){ // clear and full the gap. while( (r>l) && (Q[r-1].c > route[i].c) ) r--; s = 0; for(int j = l; j < r; j++) s += Q[j].gap; pre.c = route[i].c; pre.gap = k-s; Q[r++] = pre; // use the gap from the cheapest. s = 1LL*route[i].a*route[i].b; while( (l<r) && (Q[l].gap <= s) ){ res += Q[l].gap*Q[l].c; s -= Q[l++].gap; } if( s > 0 ){ res += s*Q[l].c; Q[l].gap -= s; } } printf("%lld\n", res); } int main(){ int T; scanf("%d", &T); while( T-- ){ scanf("%d%lld", &n,&k); if( init() ) solve(); else printf("Impossible\n"); } return 0; }
E - Ever Dream
题意: 给N行字符, 然后统计单词出现频率. 输出频率大于1的, 最长单词,当有多个,则输出倒数第二个.
解法: 跟着模拟即可. 用STL来写.比较方便. 对于输入的处理, 将非英文字符全部换成空格,再利用strtok来分割.这样就蛮方便了.
#include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<map> #include<vector> #include<algorithm> using namespace std; map<string,int> mp; bool isletter( char ch ){ if( ( (ch>='a')&&(ch<='z') ) || ( (ch>='A')&&(ch<='Z') ) ) return true; return false; } bool Upletter( char ch ){ if( (ch>='A') && (ch<='Z') ) return true; return false; } struct node{ string s; int len; bool operator < (const node &tmp) const{ return (len>tmp.len)||((len==tmp.len)&&(s<tmp.s)); } }nxt; vector< node > Q[110]; void solve(){ int n; char str[110]; scanf("%d", &n); getchar(); mp.clear(); for(int i = 0; i < n; i++){ memset(str, 0, sizeof(str)); gets( str ); int L = strlen(str); for(int i = 0; str[i]; i++){ if( isletter( str[i] ) ){ if( Upletter( str[i] ) ) str[i] += 32; } else str[i] = ' '; } char *p = strtok( str, " " ); while( p ){ if( mp.count(p) == 0 ) mp[p] = 1; else mp[p] += 1; p = strtok( NULL, " " ); } } for(int i = 0; i <= 100; i++) Q[i].clear(); for( map<string,int>::iterator it = mp.begin(); it != mp.end(); it++ ){ nxt.s = it->first; nxt.len = (it->first).size(); Q[ it->second ].push_back( nxt ); } for(int i = 0; i <= 100; i++) sort( Q[i].begin(), Q[i].end() ); bool flag = true; for(int i = 100; i > 1; i-- ){ if( (int)Q[i].size() == 0 ) continue; /* printf("len = %d, size = %d\n", i, Q[i].size() ); for(int j = 0; j < (int)Q[i].size(); j++ ){ printf("%s ", Q[i][j].s.c_str() ); } puts(""); */ int cur = 1; for(int j = 1; j < (int)Q[i].size(); j++ ){ if( Q[i][j].len == Q[i][j-1].len ) cur++; else break; } if( flag ) flag = false; else printf(" "); if( cur == 1 ) printf("%s", Q[i][0].s.c_str() ); else printf("%s", Q[i][cur-2].s.c_str() ); } puts(""); } int main(){ freopen("1.in","r",stdin); int T; scanf("%d", &T); while( T-- ){ solve(); } return 0; }
F - Fawful's Revenge
据说此题很神....
G - Gibonacci number
题意: G(i) = G(i-1) + G(i-2), 现在给出 G(0) = 1, G(i) , i, 让求 G(j), 当 G(1) > 0 , 否则输出 -1.
解法: G(i) = F(i-1)*G(1) + F(i-2) , 带入计算即可. 注意判定是否合法.
#include<cstdio> typedef long long LL; LL f[25], G[25]; int main(){ f[0] = f[1] = 1; for(int i = 2; i <= 20; i++) f[i] = f[i-1]+f[i-2]; int T, i, gi, j; scanf("%d", &T); while( T-- ){ scanf("%d%d%d", &i,&gi,&j); // i, Gi, j if( i == 1 ){ if( gi < 1 ){ printf("-1\n"); continue; } if( j == 1 ) printf("%d\n", gi ); else printf("%lld\n", f[j-1]*gi+f[j-2] ); } else{ if( (gi-f[i-2])%f[i-1] != 0 ) printf("-1\n"); else{ LL g1 = (gi-f[i-2])/f[i-1]; if( g1 < 1 ){ printf("-1\n"); continue; } if( j == 1 ) printf("%lld\n", g1); else printf("%lld\n", f[j-1]*g1+f[j-2] ); } } } return 0; }
H - Happy Programming Contest
题意: N个题目,每个题目有个完成时间,有个价值,现在给定时间T,求最大完成数量,最小完成时间,最大价值.
解法: 01背包, 只是多了点条件
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int N = 1010; int num[55][1010], dp[55][1100], time[55][1100], sum[55][1100]; int n, T; struct node{ int t, c; bool operator < (const node &tmp)const{ return (t<tmp.t)||(t==tmp.t&&c>tmp.c); } }p[55]; int main(){ int T, t; scanf("%d", &t); while( t-- ){ scanf("%d%d", &T, &n); for(int i = 1; i <= n; i++) scanf("%d", &p[i].t); for(int i = 1; i <= n; i++) scanf("%d", &p[i].c); sort( p+1, p+n+1 ); memset( dp, 0, sizeof(dp)); memset( num, 0, sizeof(num)); memset( sum, 0, sizeof(sum)); for(int i = 1; i <= n; i++){ for(int j = 0; j <= T; j++){ // dp[i][j] = max( dp[i-1][j], dp[i-1][j-p[i].t]+p[i].c ); dp[i][j] = dp[i-1][j]; num[i][j] = num[i-1][j]; sum[i][j] = sum[i-1][j]; if( j >= p[i].t ){ if( dp[i][j] <= (dp[i-1][j-p[i].t]+p[i].c) ){ if( dp[i][j] < dp[i-1][j-p[i].t]+p[i].c ){ dp[i][j] = dp[i-1][j-p[i].t]+p[i].c; num[i][j] = num[i-1][j-p[i].t]+1; sum[i][j] = sum[i-1][j-p[i].t] + j; } else if( num[i][j] <= num[i-1][j-p[i].t]+1 ){ if( num[i][j] < num[i-1][j-p[i].t]+1 ){ num[i][j] = num[i-1][j-p[i].t]+1; sum[i][j] = sum[i-1][j-p[i].t] + j; } else if( sum[i][j] > sum[i-1][j-p[i].t] + j ){ sum[i][j] = sum[i-1][j-p[i].t] + j; } } } } } } int pc = 0, pn = 0, pt = 0; for(int i = 0; i <= T; i++){ if( pc < dp[n][i] ){ pc = dp[n][i];pn = num[n][i];pt = sum[n][i]; } else if( pc == dp[n][i] ){ if( pn < num[n][i] ) pn = num[n][i], pt = sum[n][i]; else if( pt > sum[n][i] ) pt = sum[n][i]; } } printf("%d %d %d\n", pc, pn, pt ); } return 0; }
I - I am Nexus Master!
题意: 论坛有10个等级, 一个用户的等级评定,通过使用 注册时间,下载量,上传量, 上传/下载 比例来恒定.给你当前一个用户的信息,然后反馈其等级.
解法: 顺着模拟即可. 大致是 先判定是否直接降级到0, 否则再判定是否降级, 若没有 再判定是否升级.
#include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> using namespace std; const double esp = 1e-8; const string tar[] = { "Peasant", // 0 "User", // 1 "Power_User", // 2 "Elite_User", // 3 "Crazy_User", // 4 "Insane_User", // 5 "Veteran_User", // 6 "Extreme_User", // 7 "Ultimate_User", // 8 "Nexus_Master" // 9 }; double A[10]={ 0,0,50,120,300,500,750,1024,1.5*1024,3*1024 }; double B[10]={ 0,0,1.05,1.55,2.05,2.55,3.05,3.55,4.05,4.55 }; int C[10]={ 0,0,4,8,15,25,40,60,80,100 }; int sign(double x){ return x<-esp?-1:(x>esp); } string s; int week, cur; double down, up; void input(){ char tmp[20]; scanf("%s %d %lf %lf", tmp, &week, &down, &up ); s = tmp; for(int i = 0; i < 10; i++) if( s == tar[i] ){ cur = i; break; } } bool Clear(){ if( (sign(down-50 ) >= 0) && (sign(up-0.4*down) < 0) ) return true; if( (sign(down-100) >= 0) && (sign(up-0.5*down) < 0) ) return true; if( (sign(down-200) >= 0) && (sign(up-0.6*down) < 0) ) return true; if( (sign(down-400) >= 0) && (sign(up-0.7*down) < 0) ) return true; if( (sign(down-800) >= 0) && (sign(up-0.8*down) < 0) ) return true; return false; } bool Down(){ //可连续下降 bool flag = false; while( (cur>1) && (sign(up - down*(B[cur]-0.1)) < 0) ) cur -= 1, flag = true; return flag; } void Up(){ //若降级过,则必定不可能升级 int x = 9; for(int x = 9; x > cur; x-- ){ if( (x>cur) && (week>=C[x]) && (sign(down-A[x])>=0) && (sign(up-down*B[x])>=0) ){ cur = x; break; } } } int main(){ int T; scanf("%d", &T); while( T-- ){ input(); if( Clear() ) printf("%s\n", tar[0].c_str() ); else{ if( Down() == false ) Up(); printf("%s\n", tar[cur].c_str() ); } } return 0; }