湖南工业大学个人选拔赛第一场 解题报告
2013-04-14 00:20
381 查看
Problem A 客户数量
可以得出将长度为 len的蛋糕切成 len段 1的,不管如何切,总花费都为 len*(len-1)/2
对于每个顾客,来到的时间st,以及需要的蛋糕长度k,则有一个终止时间ed = st+k*(k-1)/2
对每个顾客求出其服务终止时间,然后得到n个区间,将区间以终点排序,然后线性扫过去,用i的起点时间与前一个服务顾客
的终点时间比较即可。
时间复杂度 O( NlogN )
Problem B 最小整齐度
dp(i,j) 表示前i个单词,最后一行字符数量为j的,最小整齐度。其中最后一行不计算花费
则若已知 dp(i,j) 的情况下,添加第 i+1 个单词进来只会导致两种情况:
1,放于最后一行,不换行,此时不增加花费。
2,换行,此时花费增加 (M-j)^3
则状态方程为
情况一: dp( i, j+1+L_(i+1) ) = Min{ dp(i,j) }
情况二: dp( i, L_(i+1) ) = Min{ dp(i,j) + (M-j)^3 }
注意两种情况成立的 条件判定。
Problem C 最小覆盖点
我们可以令 每个区间的 [l,r] ,l 顶点为-1, r顶点为 +1, 则只有当 x < l or x >= r 时候 前缀和才会为0.
这样将时间复杂度将为 O(N)
当然也可用用 线段树 or 树状数组 O( NlogN ) 的写法,同样能过
Problem D 1的数量
组合数学, 通过计算 [1,n]区间数量解决.
假设n十进制表示位数为L,则通过统计长度为 L, L-1, ..., 1的数量来得出结果.
假设 n = a1,a2,a3,...,aL // 十进制表示,其中a1为高位
一.当长度小于L, 假设其为 len = 1,2,...,L-1
则当前数 X = a1,a2,a3,...,a_len
因为 Length(X) < Length(N), 所以任意的X都小于N,
我们可以枚举 X中包含的1的个数,假设其为 K, 则 K = 0,1,2,...,len
这里考虑, 因为高位不能为0,而其它位可以为 0.
这里分两种情况:
1. a_1 为1, 则 a_2,..,a_len ,中有 k-1位取1, 有 n-k位不取1,
对于不取1的位置,其可以取(0,2,3,..,9) ,所以方案数为 C(n-1,k-1) * 9 ^ (n-k).
1. a_1 不为1, 则 a_2,...,a_len,中有 k个为取1, 有n-k位不取1,
对于不取1的位置, 若是a_1位置则只能取(2,3,..,9)共8种,而a_i ( i != 1 ) 则可以取( 0,2,..,9 )共9种,所以方案数位 8*9^(n-1-k)*C(n-1,k)
而 k 的取值范围为 (0,1,2,..,len )
则总和为
注意 k =
len 则要单独计算因为没有 a_1不为1的情况。
二.当长度等于L
若当前数 X = a1,a2,a3,...,a_L
我们则一位一位处理, 当我们处理当 第i位, 则 (1,i-1)位 为1的数量为 cnt
若 a_i < 0 , 则当前数 X 要小于 N, 则当前位只能取0.
若 a_i = 1 , 则当前数 X 要小于 N, 则当前位可以取0
则 X' = a_1,a_2,...,a_i = 0, a_(i+1)..., a_L
此时, 对于 a_(i+1) ,..., a_L 位(共有L-i位)而言,取任意数都 会使 X' < N , 意味着都满足要求.
则我们可以通过枚举 此区间1的个数, 假设其为 k = 0,1,2,.., L-i
则方案数为 (cnt+k)*C(L-i, k)*9^(L-i-k)
若 a_i > 1 , 则当前数 X 要小于 N, 则当前位可以取 ( 0,1,2, ..., (a_i-1) )
这里其实和 a_i = 1 计算方式差不多, 只是要注意.
若我们取 a_i = 1, 则 1的位数统计的时候就要多一个 即 cnt+1+k
对于 a_i 不取 1, 则为 cnt+k ...
Problem E
暴力枚举得到的组合情况 总共为 9! = 3*10^6, 然后对于和适度可以边枚举边计算,然后优化掉比较的时间。
所以总时间复杂为 O( 9! ) = O( 10^6 )
转至http://www.cnblogs.com/yefeng1627/archive/2013/04/12/3016398.html
可以得出将长度为 len的蛋糕切成 len段 1的,不管如何切,总花费都为 len*(len-1)/2
对于每个顾客,来到的时间st,以及需要的蛋糕长度k,则有一个终止时间ed = st+k*(k-1)/2
对每个顾客求出其服务终止时间,然后得到n个区间,将区间以终点排序,然后线性扫过去,用i的起点时间与前一个服务顾客
的终点时间比较即可。
时间复杂度 O( NlogN )
#include<cstdio> #include<cstring> #include<algorithm> #include<cassert> using namespace std; const int N = 1e5+10; typedef long long LL; const int Max = (int)(1e9); struct node{ LL st, ed, k; bool operator < ( node tmp ) const{ if( ed == tmp.ed ) st > tmp.st; return ed < tmp.ed; } void input(){ scanf("%lld%lld", &st, &k); assert( st>=0 && st<=Max ); assert( k>=0 && k<=Max ); ed = st + k*(k-1)/2; } }p ; bool vis ; int n; int main(){ freopen("2.in","r",stdin); freopen("2.out","w",stdout); while( scanf("%d",&n) != EOF ){ assert( n >= 0 && n <= 100000 ); int k, ans = 0; int x, t; for(int i = 0; i < n; i++) p[i].input(); sort( p, p+n ); int idx = 0; memset( vis, 0, sizeof(vis)); for(int i = 0; i < n; i++){ if( p[i].st >= idx ){ if( p[i].st == p[i-1].st && p[i].ed == p[i-1].ed ){ continue; } vis[i] = 1; idx = p[i].ed; ans++; } } printf("%d\n", ans ); } return 0; }
Problem B 最小整齐度
dp(i,j) 表示前i个单词,最后一行字符数量为j的,最小整齐度。其中最后一行不计算花费
则若已知 dp(i,j) 的情况下,添加第 i+1 个单词进来只会导致两种情况:
1,放于最后一行,不换行,此时不增加花费。
2,换行,此时花费增加 (M-j)^3
则状态方程为
情况一: dp( i, j+1+L_(i+1) ) = Min{ dp(i,j) }
情况二: dp( i, L_(i+1) ) = Min{ dp(i,j) + (M-j)^3 }
注意两种情况成立的 条件判定。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cassert> #include<algorithm> using namespace std; #define cmp(x) (x)*(x)*(x) typedef long long LL; const LL inf = ~0llu>>1; LL dp[2][510]; int L[2010]; int n, m; void DP(){ memset( dp, 0xff, sizeof(dp) ); // for(int i = 0; i < m; i++) dp[0][i] = inf; dp[0][0] = 0; for(int i = 0; i < n; i++){ int cur = i&1, nxt = (i+1)&1; for(int j = 0; j < m; j++) dp[nxt][j] = -1; for(int j = 0; j < m; j++){ if( (dp[cur][j] != -1) ){ if( j == 0 ){ int len = (j+L[i+1] == m )? 0 : j+L[i+1]; if( dp[nxt][len] == -1 ) dp[nxt][len] = dp[cur][j]; else dp[nxt][len] = min( dp[nxt][len], dp[cur][j] ); } else if( j+1+L[i+1] <= m ){ int len = (j+1+L[i+1] == m) ? 0 : j+1+L[i+1]; if( len < m ){ if( dp[nxt][len] == -1 ) dp[nxt][len] = dp[cur][j]; else dp[nxt][len] = min( dp[nxt][len], dp[cur][j] ); } } if( j > 0 ){ int k = (L[i+1] == m) ? 0 : L[i+1]; if( dp[nxt][ k ] == -1 ) dp[nxt][ k ] = dp[cur][j] + cmp(m-j); else dp[nxt][ k ] = min( dp[nxt][ k ], dp[cur][j] + cmp(m-j) ); } } } //for(int j = 0; j < m; j++){ // printf("%lld ", dp[nxt][j] ); //} puts(""); } LL ans = -1; for(int i = 0; i < m; i++){ // printf("%lld\n", dp[n&1][i] ); if( dp[ n&1 ][i] != -1 ){ if( ans == -1 ) ans = dp[n&1][i]; else ans = min( ans, dp[n&1][i] ); } } if(ans == -1) ans = 0; printf("%lld\n", ans ); } int main(){ freopen("2.in","r",stdin); freopen("2.out","w",stdout); while( scanf("%d%d", &n,&m) != EOF){ assert( n>=1 && n<=2000 ); assert( m>=1 && m<=500 ); for(int i = 1; i <= n; i++){ scanf("%d", &L[i] ); assert( L[i]>=1 && L[i]<=m ); } DP(); } return 0; }
Problem C 最小覆盖点
我们可以令 每个区间的 [l,r] ,l 顶点为-1, r顶点为 +1, 则只有当 x < l or x >= r 时候 前缀和才会为0.
#include<stdio.h> #include<math.h> #include<string.h> #include<algorithm> #include<assert.h> using namespace std; const int N = 2e5+10; int t ,n, m,ans; main() { freopen("3.in","r",stdin); freopen("3.out","w",stdout); while(scanf("%d%d",&n,&m) !=EOF ) { assert( n>=1 && n<=20000 ); assert( m>=1 && m<=50000 ); ans=0; memset(t,0,sizeof(t)); int a, b, j = 0; for(int i = 0; i < m; i++){ scanf("%d%d",&a,&b); if( a > b ) swap(a,b); assert( a>=1 && a<=n ); assert( b>=1 && b<=n ); t[a] += 1; t[b] -= 1; } for(int i = 1; i <= n; i++ ) { j += t[i]; if( j == 0 ) ans++; } printf("%d\n",ans); } return 0; }
这样将时间复杂度将为 O(N)
当然也可用用 线段树 or 树状数组 O( NlogN ) 的写法,同样能过
Problem D 1的数量
组合数学, 通过计算 [1,n]区间数量解决.
假设n十进制表示位数为L,则通过统计长度为 L, L-1, ..., 1的数量来得出结果.
假设 n = a1,a2,a3,...,aL // 十进制表示,其中a1为高位
一.当长度小于L, 假设其为 len = 1,2,...,L-1
则当前数 X = a1,a2,a3,...,a_len
因为 Length(X) < Length(N), 所以任意的X都小于N,
我们可以枚举 X中包含的1的个数,假设其为 K, 则 K = 0,1,2,...,len
这里考虑, 因为高位不能为0,而其它位可以为 0.
这里分两种情况:
1. a_1 为1, 则 a_2,..,a_len ,中有 k-1位取1, 有 n-k位不取1,
对于不取1的位置,其可以取(0,2,3,..,9) ,所以方案数为 C(n-1,k-1) * 9 ^ (n-k).
1. a_1 不为1, 则 a_2,...,a_len,中有 k个为取1, 有n-k位不取1,
对于不取1的位置, 若是a_1位置则只能取(2,3,..,9)共8种,而a_i ( i != 1 ) 则可以取( 0,2,..,9 )共9种,所以方案数位 8*9^(n-1-k)*C(n-1,k)
而 k 的取值范围为 (0,1,2,..,len )
则总和为
注意 k =
len 则要单独计算因为没有 a_1不为1的情况。
二.当长度等于L
若当前数 X = a1,a2,a3,...,a_L
我们则一位一位处理, 当我们处理当 第i位, 则 (1,i-1)位 为1的数量为 cnt
若 a_i < 0 , 则当前数 X 要小于 N, 则当前位只能取0.
若 a_i = 1 , 则当前数 X 要小于 N, 则当前位可以取0
则 X' = a_1,a_2,...,a_i = 0, a_(i+1)..., a_L
此时, 对于 a_(i+1) ,..., a_L 位(共有L-i位)而言,取任意数都 会使 X' < N , 意味着都满足要求.
则我们可以通过枚举 此区间1的个数, 假设其为 k = 0,1,2,.., L-i
则方案数为 (cnt+k)*C(L-i, k)*9^(L-i-k)
若 a_i > 1 , 则当前数 X 要小于 N, 则当前位可以取 ( 0,1,2, ..., (a_i-1) )
这里其实和 a_i = 1 计算方式差不多, 只是要注意.
若我们取 a_i = 1, 则 1的位数统计的时候就要多一个 即 cnt+1+k
对于 a_i 不取 1, 则为 cnt+k ...
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<map> #include<string> #include<cmath> #include<cassert> using namespace std; const int mod = 1e9+7; typedef unsigned long long LL; const LL Max = (LL)(1000000000000000000ll); LL C[20][20], fact[20]; void init(){ for(int i = 0; i <= 18; i++) C[i][0] = C[i][i] = 1; for(int i = 2; i <= 18; i++ ){ for(int j = 1; j < i; j++) C[i][j] = (C[i-1][j-1] + C[i-1][j])%mod; } fact[0] = 1; for(int i = 1; i <= 18; i++) fact[i] = fact[i-1]*9%mod; } LL sum( LL x ){ LL res = 0; if( x <= 0 ) return res; int a[20], n = 0; LL t = x; while( t ){ t/=10; n++; } t = x; for(int i = n; i >= 1; i-- ){ a[i] = t%10; t /= 10; } for(int L = 1; L <= n-1; L++ ){ res += L; for(int k = 1; k < L; k++){ res += 1LL*k*( fact[L-k]*C[L-1][k-1] + 8LL*fact[L-1-k]*C[L-1][k] )%mod; } } // printf("res = %I64u\n", res ); //Accepted int c = 0; LL tmp = res; for(int i = 1; i <= n; i++){ if( a[i] == 1 ){ if( i > 1 ){ LL tmp = 0; for(int j = 0; j <= n-i; j++){ tmp += (c+j)*( C[n-i][j]*fact[n-i-j] ); tmp %= mod; } res = (res+tmp)%mod; } c++; } else if( a[i] > 1 ){ LL tmp = 0; for(int j = 0; j <= n-i; j++){ tmp += (c+1+j)*( C[n-i][j]*fact[n-i-j] ); if( i == 1 ) tmp += (a[i]-2)*(c+j)*( C[n-i][j]*fact[n-i-j] )%mod; else tmp += (a[i]-1)*(c+j)*( C[n-i][j]*fact[n-i-j] )%mod; tmp %= mod; } // printf("tmp = %I64u\n", tmp ); res += tmp; res %= mod; } } // printf("add = %I64u\n", res+c - tmp ); // Wrong Answer 1248 return (res + c)%mod; } int main(){ // freopen("3.in","r",stdin); // freopen("3.out","w",stdout); init(); LL a , b; while( scanf("%llu%llu", &a,&b) != EOF ){ if( a > b ) swap( a, b ); assert( a >= 1 && a <= Max ); assert( b >= 1 && b <= Max ); printf("%llu\n", (sum(b)-sum(a-1)+mod)%mod ); } return 0; }
Problem E
暴力枚举得到的组合情况 总共为 9! = 3*10^6, 然后对于和适度可以边枚举边计算,然后优化掉比较的时间。
所以总时间复杂为 O( 9! ) = O( 10^6 )
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #define MIN(a,b) (a)<(b)?(a):(b) const int inf = 0x3fffffff; const int Mod = 1000000; const int Max = (int)(1e9); int g[15][15], a[15], n, len; int ans; int b[15]; bool vis[10]; void dfs( int num, int sum ){ if( sum > ans ) return; if( num == len ){ if( sum < ans ) ans = sum; return; } for(int i = 0; i < len; i++){ if( !vis[i] ){ if( num == 0 ){ if( a[i] == 0 ) continue; vis[i] = 1; b[num] = a[i]; dfs( num+1, 0 ); vis[i] = 0; } else{ // num > 0 if( sum + g[a[i]][b[num-1]] < ans ){ vis[i] = 1; b[num] = a[i]; dfs( num+1, sum+g[a[i]][b[num-1]] ); vis[i] = 0; } } } } } int main(){ freopen("4.in","r",stdin); freopen("4.out","w",stdout); int T, X; scanf("%d", &T); while( T-- ){ scanf("%d", &X );assert( X>=1 && X<=Max ); for(int i = 0; i < 10; i++) for(int j = 0; j < 10; j++ ){ scanf("%d", &g[i][j] ); assert(g[i][j]>=0 && g[i][j]<=Mod); } ans = inf;len = 0; int t = X; while( t ){ a[len++] = t%10; t /= 10; } memset( vis, 0, sizeof(vis) ); dfs( 0, 0 ); printf("%d\n", ans ); } return 0; }
转至http://www.cnblogs.com/yefeng1627/archive/2013/04/12/3016398.html
相关文章推荐
- 湖南工业大学个人选拔赛第一场 解题报告
- (非本校)湖南工业大学个人选拔赛第一场 解题报告
- (非本校)湖南工业大学个人选拔赛第一场 解题报告
- 湖南工业大学个人选拔赛第一场 解题报告
- 湖南工业大学个人选拔赛第二场 解题报告
- 湖南工业大学个人选拔赛第二场 解题报告
- 湖南工业大学个人选拔赛第三场 解题报告(自己的)
- 湖南工业大学个人选拔赛第三场 解题报告(自己的)
- 湖南工业大学个人选拔赛第二场 解题报告
- 湖南工业大学个人选拔赛第三场 解题报告
- 湖南工业大学个人选拔赛第三场 解题报告
- 湖南工业大学个人选拔赛第一场 解题报告
- 湖南工业大学个人选拔赛第二场 解题报告
- 湖南工业大学个人选拔赛第一场 题解
- 多校第一场CSUST 个人解题报告
- 08-22成都大学ACM集训个人赛第一场解题报告
- 珠海邀请赛个人解题报告 Problem I: Hanoi Tower Once More
- 【解题报告】13级个人结业赛(二) ——动(dou)态(bu)规(hui)划(zuo)专场
- XTU (湘潭大学) 2011 新生练习赛(第一场)/ 解题报告 4.4
- 个人赛Practice(1)解题报告