A 题: 越来越弱,都掉渣了....连暴力递归模拟都不会写了...对每个黑棋递归搜索其边界,若遇到相同的黑棋则数量+1继续找,遇到0或者边界则直接return.

#include<cstdio> #include<cstdlib> #include<cstring> char mp[110][110]; bool flag; int n, m, dir[4][2] = {0,1,0,-1,1,0,-1,0}; bool legal(int x,int y){ if( x>=0&&x<n&&y>=0&&y<m) return true; return false; } int find(int x,int y){ mp[x][y] = '1'; int s = 0; for(int i = 0; i < 4; i++){ int xx = x+dir[i][0], yy = y+dir[i][1]; if( legal(xx,yy) ){ if( mp[xx][yy] == '0' ) flag = false; else if( mp[xx][yy] == '2' ) s += find(xx,yy); } else flag = false; } if( flag ) return s+1; return 0; } int main(){ while( scanf("%d%d",&n,&m) != EOF){ for(int i = 0; i < n; i++) scanf("%s", mp[i] ); int s = 0; for(int i = 0; i < n; i++) for(int j = 0; j < m; j++){ if( mp[i][j] == '2' ){ flag = true; s += find(i, j); } } printf("%d\n", s ); } return 0; }
B题: 简单DP,一个状态dp(i,j) 由左右下,三个方向传递而来,所以dp(i,j) = min( dp(i,j-1), dp(i,j+1), dp(i-1,j) ) + cost(i,j)

#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; int n, m; int dp[25][25]; int val[25][25]; int main(){ while( scanf("%d%d", &n,&m) != EOF){ memset( dp, 0x3f, sizeof(dp)); memset( val, 0x3f, sizeof(val)); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &val[i][j] ); for(int i = 1; i <= m; i++ ) dp[n][i] = val[n][i]; for(int i = n-1; i >= 1; i--){ for(int j = 1; j <= m; j++) dp[i][j] = dp[i+1][j] + val[i][j]; for(int j = 2; j <= m; j++) dp[i][j] = min( dp[i][j], dp[i][j-1]+val[i][j] ); for(int j = m-1; j >= 1; j--) dp[i][j] = min( dp[i][j], dp[i][j+1]+val[i][j] ); } int res = 0x3f3f3f3f; for(int i = 1; i <= m; i++) res = min(res, dp[1][i] ); printf("%d\n", res ); } return 0; }
C题: 最大子矩阵和, 首先枚举起始列(1,n), 然后枚举长度, 计算长度的时候从小到大,这样就可以利用前面求得的和.然后再将每一行看作一个点,
求一维的最大连续和. 总时间复杂度为 O(N^3)

#include<stdio.h> #include<stdlib.h> #include<string.h> const int inf = 0x3fffffff; int a[110][110], b[110]; int dp[110], ans, n; int main() { while( scanf("%d", &n) != EOF) { for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) scanf("%d", &a[i][j]); ans = -inf; for(int i = 1; i <= n; i++) { memset( b, 0, sizeof(b)); for(int j = i; j <= n; j++) { for(int k = 1; k <= n; k++) b[k] += a[j][k]; int tmp = -inf; for(int k = 1; k <= n; k++) { if( tmp < 0 ) tmp = b[k]; else tmp += b[k]; if(tmp > ans) ans = tmp; } } } printf("%d\n", ans ); } return 0; }
D题: 贪心, 对于连续不为0长度大于等于三的 一列题目, 我们知道做其中一个i, 并使其 cost(i-1) + cost(i) + cost(i+1) 尽可能大,总是最优. 基于此的贪心即可.

#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; struct node{ int x, c, idx; }t; int a[110], n; int main(){ while( scanf("%d",&n) != EOF){ for(int i = 0; i < n; i++){ scanf("%d", &a[i] ); } int idx = -1, res = 0; for(int i = 0; i < n; i++) if( a[i] ) { idx = i; break; } while( idx != -1 ){ int l = idx, r = l; while( (r+1) < n && a[r+1] ) r++; //printf("l = %d, r = %d\n", l, r ); //for(int i = 0; i < n; i++) printf("%d ",a[i]); puts(""); if( r-l+1 <= 2 ){ res += max( a[l], a[r] ); a[l] = a[r] = 0; } else{ bool find = false; for(int i = l+1; i <= r-1; i++){ if( find == false ){ find = true; t.idx = i; t.x = a[i]; t.c = min(a[i-1],a[i])+a[i]+min(a[i+1],a[i]); if( 3*t.x == t.c ) break; } else{ int x = a[i], c = min(a[i-1],a[i])+a[i]+min(a[i+1],a[i]); if( 3*x == c ){ t.idx = i; t.c = c; t.x = x; break; } if( (c>t.c)||((c==t.c)&&(x<t.x))){ t.idx = i; t.c = c; t.x = x; } } } res += t.x; int pos = t.idx;// printf("pos = %d\n", pos ); a[ pos-1 ] -= min( a[pos-1],t.x ); a[ pos ] -= t.x; a[ pos+1 ] -= min( a[pos+1],t.x ); } idx = -1; for(int i = 0; i < n; i++) if(a[i]){idx=i;break;} } printf("%d\n", res ); } return 0; }
E题: 简单计算集合, 可是我老WA,各种无奈. 解法是这样的. 若我们假定两个矩形 A,B ,其中A在左边, B在右边. (若给的A在右,我们替换AB即可)
这时候不同的情况有两种:
一: A 与 B 的区间相交
x区间相交, 则A在下B在上时,距离为( B.y1 - A.y2 ), 否则( A.y1 - B.y2 )
y区间相交, 则因为A必定是在左侧,则距离为 (B.x1 - A.x2)
二: A 与 B 的区间不相交
若A在下,B在上, 则距离为 dist( A(x2,y2), b(x1,y1) ),否则 dist( A(x2,y1), B(x1,y2) )

#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> using namespace std; const double eps = 1e-8; struct node{ double x1,y1,x2,y2; void input(){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); } }p1,p2; int sign( double x ){ return x<-eps ? -1 : (x>eps); } bool legal( double a,double b, double x1, double x2 ){ // x1 <= a <= x2 || x1 <= b <= x2 if( (sign(a-x1)>=0 && sign(x2-a)>=0) || (sign(b-x1)>=0 && sign(x2-b)>=0) ) return true; return false; } double dist(double x1,double y1,double x2,double y2){ return sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) ); } double solve(){ if( sign(p1.x1-p2.x1) >= 0 ) swap( p1, p2 ); if( legal(p1.y1,p1.y2,p2.y1,p2.y2) || legal(p2.y1,p2.y2,p1.y1,p1.y2)) return abs(p2.x1 - p1.x2); if( legal(p1.x1,p1.x2,p2.x1,p2.x2) ||legal(p2.x1,p2.x2,p1.x1,p1.x2) ){ if( sign(p2.y1-p1.y1)>=0 ) return abs(p2.y1 - p1.y2); else return abs(p1.y1 - p2.y2); } if( sign(p2.y1-p1.y1) >= 0 ) return dist( p1.x2, p1.y2, p2.x1, p2.y1 ); else return dist( p1.x2, p1.y1, p2.x1, p2.y2 ); } int main(){ int T; scanf("%d", &T); while( T-- ){ p1.input(); p2.input(); double ans = solve(); printf("%.4f\n", ans ); } return 0; }
F题: 感谢出题人的点拨.学会了这道题. 再次Orz一下出题人...
我们观察这样 n = 2, k = 7 的情况来说明题目解法:
11 --1 //0个0
101 --2 // 1个0
110 --3
1001 --4 // 2个0
1010 --5
1100 --6
10001 --7 //3个0
10010 --8
10100 --9
11000 --10
假定0的数量为 r, 因为1的数量n, 则长度为 r+n的这段排列的方案有, \binom{ n+r-1 }{ r-1 } , (1之间的空隙,看作有区别盒子,0看作无区别球.方案数为\binom{n+r-1}{r-1})
从1 开始到第k大, 我们要找到第一个满足 s = \sum \binom{n+r-1}{r-1} > k , 的这个s, 其中r为0的数量.
当找到第一个大于k的s后, 当前长度最小的形式为 01...10..0 , 其中前面 n个1, 后面r-1个0. 然后进行全排列.

#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; int p[1010]; int Binom( int a, int b ){ int s = 1; for(int i = 0; i < a; i++) s = s*(b-a+1+i)/(i+1); return s; } int main(){ int n, k, T; scanf("%d", &T); while( T-- ){ scanf("%d%d", &k,&n); if( n == 1 ){ printf("1"); for(int i = 0; i < k-1; i++)printf("0");puts(""); continue; } int s = 0, r = 0; while(1){ int t = Binom( n-1, n+r-1 ); if( s+t > k ) break; // 1000...01..1 format s += t; r++; } p[0] = 0; for(int i = 1; i <= n; i++) p[i] = 1; for(int i = n+1; i < n+r; i++) p[i] = 0; r = n+r; while( s < k ) s++,next_permutation( p, p+r ); if(p[0]) printf("%d",p[0]); for(int i = 1; i < r; i++) printf("%d",p[i]);puts(""); } return 0; }
X题: 神题..看都没看...
Y题:
对于这题,我们假设只有两个含N个元素的数组, A,B, 求从两数组中各取一个相加,求其前N小.,假定A,B数组有序,根据单调性有:
A1+B1 <= A1+B2 <= A1+B3 <= ... <= A1+Bn
A2+B1 <= A2+B2 <= A2+B3 <= ... <= A2+Bn
...
An+B1 <= An+B2 <= An+B3 <= ... <= An+Bn
对于 Ai+Bj而言, 从其位置(i,j)出发(单一变量原则) 下一个(向右,向下)最小值,只可能是(AI+1, Bj) 或(Ai,Bj+1).
那么,我们首先把 A1+B1 , A1+B2 , ... ,A1+Bn放到堆中, 将所有右侧最小值先放入,这样每次取出最小元素A(x,y)时,只需要
将其下方元素再加入堆即可,A(x+1,y), 因为若 已经取到A(x,y), 则A(x,1) - A(x,y-1) 必定都已被取. 而A(x-1,y+1)若还未取,而取A(x,y+1)
不符合, 因为 A(x-1,y+1) <= A(x,y+1) ,由此可知此时将 A(x+1,y)放入堆中即可. 维护堆中一直只有N个元素,总时间复杂度为 NlogN.
再回到本题, 因为总共N行, 每行N个元素, 那么我们将N行 二分归并下去, 划分成 2行,1行, 形式,组合之后再合并.即可.
时间复杂度为 N(logN)^2

#include<cstdio> #include<cstdlib> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int N = 500; int val[N][N], n; struct node{ int x, y, c; bool operator <(node tmp)const{ return c > tmp.c; } }t; int *cmp( int *A, int *B ){ int *res = new int[n]; priority_queue<node>Q; while( !Q.empty() ) Q.pop(); for(int i = 0; i < n; i++){ t.x = 0, t.y = i; t.c = A[0] + B[i]; Q.push(t); } for(int i = 0; i < n; i++){ t = Q.top(); Q.pop(); res[i] = t.c; t.x++; t.c = A[t.x] + B[t.y]; Q.push(t); } return res; } int *solve(int l,int r){ int *res = new int[n]; if( l == r ) return val[l]; if( r-l+1 == 2 ) res = cmp( val[l], val[r] ); else{ int m = (l+r)>>1; int *r1 = solve( l, m ), *r2 = solve( m+1, r ); res = cmp( r1, r2 ); } return res; } int main(){ while( scanf("%d", &n) != EOF){ for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++) scanf("%d", &val[i][j] ); sort( val[i], val[i]+n ); } int *res = solve(0,n-1); for(int i = 0; i < n; i++) printf(i==0?"%d":" %d",res[i]); puts(""); } return 0; }
Z题: 没写出来..
想到了一个O(N^2)的DP, 先按(x,y)从小到大排序, 然后 dp(i) = max{ dp(k-1) + cost( k, i ) } ..TLE.
然后又想了个线性的贪心, 排序, 枚举起点i, 找个最大的j, 满足 max( Wi...Wj ) * max( Hi,..,Hj) >= Sum( Load_i, Load_j ),
则此段 Load( i, j ) 必定连起来买更划算. WA了... 坐等解题报告.