第一轮因为没有时间就没做了。据说较第二轮难一些。
第二轮题目确实比较水。
A题 高斯消元,具体思路还不完全会。待之后补充。
B题 二分答案+并查集。
C题 简单dp
D题 暴力枚举即可。
简单分析及代码:
B:题目大意是给N(N<=1000)个网页,分成k个聚类,要求类与类之间的网页的差异值至少都为t,求最大的t,每个类至少要有一个网页,差异值计算类似于几何距离。
算法:
- 计算任意两个网页的差异值,即为diff[i][j],其中最大值记为maxd,最小值mind。
- 二分答案,区间[mind,maxd],每次累加一个precision = 1e-9.
- 判断某个t值是否可以分成k个聚类,经过分析对于任意一个网页i,计有m个diff[i][j] < t ,则i与这m个需要放在一个类中,显然,这就成了合并树的问题。并查集很轻松就搞定了。
附代码如下:
- #include
- #include
- #include
- #include
- using namespace std;
- const int maxn = 1024 ;
- int n , k , father[maxn] ;
- double diff[maxn][maxn] , x[maxn] , y[maxn] , z[maxn] , mind , maxd ;
- bool vis[maxn] ;
- const double precision = 1e-9 ;
- inline double get_max(double mm,double nn) { return mm > nn ? mm : nn ; }
- inline double get_min(double mm,double nn) { return mm < nn ? mm : nn ; }
- void myUnion(int i,int j)
- {
- father[i] = j ;
- }
- int find_anc(int i) { return father[i] == i ? i : ( father[i] = find_anc(father[i]) ) ; }
- bool check(double pos)
- {
- int i , j , cnt ;
- cnt = 1 ;
- memset(vis,0,sizeof(vis));
- for( i = 1 ; i <= n ; i++) father[i] = i ;
- for ( i = 1 ; i <= n ; i++)
- {
- for( j = 1 ; j <= n ; j++) if( i != j )
- {
- if( diff[i][j] < pos ) myUnion(find_anc(i),find_anc(j));
- }
- }
- int sth = 0 ;
- for( i = 1 ; i <= n ; i++) if(!vis[j=find_anc(i)]) { sth++; vis[j] = 1 ; }
- return sth >= k ;
- }
- void solve()
- {
- int i , j ;
- double l , r , mid ;
- l = mind ; r = maxd ;
- while ( l <= r )
- {
- mid = ( l+r ) / 2.0 ;
- //判断mid是否可以构成K个类..
- if (check(mid)) l = mid+precision ;
- else r = mid - precision ;
- }
- printf("%.6lf\n",r);
- }
- int main()
- {
- int i , j ;
- while (~scanf("%d%d",&n,&k))
- {
- for ( i = 1 ; i <= n ; i++) scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
- maxd = -1.0 ; mind = 2.0 ;
- for( i = 1 ; i <= n ; i++)
- for( j = i ; j <= n ; j++)
- {
- diff[j][i] = diff[i][j] = (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]);
- mind = get_min(mind,diff[i][j]);
- maxd = get_max(maxd,diff[i][j]);
- }
- solve();
- }
- }
C题大意:有2种礼物,每个礼物有值a,b和数量k,有n(n<=1000)个人,每个人要有一个礼物且每个人也有值x,y,每个人获得礼物的满意度为a*x+b*y,求最大的满意度。
显然是个dp问题,状态方程为:
dp[i][j]表示前i个人用了j个第一种礼物。
dp[i][j] = max(dp[i-1][j-1]+第一种礼物,dp[i-1][j]+第二种礼物).
注意边界及初始条件即可。
通过分析可以发现一些单调条件,可以优化一些,附代码如下:
- #include
- #include
- #include
- #include
- using namespace std;
- const int maxn = 1024 ;
- int n , x[maxn] , y[maxn] , a , aa , b , bb , k , kk , dp[maxn][maxn] , one[maxn] , two[maxn] ;
- inline int get_max(int mm,int nn) { return mm > nn ? mm : nn ; }
- void solve()
- {
- int i , j , ans ;
- for ( i = 0 ; i <= k ; i++) dp[0][k] = 0 ;
- for( i = 1 ; i <= n ; i++) { one[i] = x[i]*a+y[i]*b; two[i] = x[i]*aa+y[i]*bb; }
- for( i = 1 ; i <= n ; i++)
- {
- if( i <= kk )
- dp[i][0] = dp[i-1][0] + two[i] ;
- else
- dp[i][0] = -1 ;
- for( j = 1 ; j <= k ; j++)
- {
- dp[i][j] = dp[i-1][j-1] + one[i] ;
- if( (i-j) <= kk ) dp[i][j] = get_max(dp[i][j],dp[i-1][j]+two[i]);
- }
- }
- for( ans = i = 0 ; i <= k ; i++) ans = get_max(ans,dp[n][i]) ;
- printf("%d\n",ans);
- }
- int main()
- {
- int i ;
- while (~scanf("%d",&n))
- {
- for ( i = 1 ; i <= n ; i++) scanf("%d%d",&x[i],&y[i]);
- scanf("%d%d%d",&k,&a,&b);
- scanf("%d%d%d",&kk,&aa,&bb);
- solve();
- }
- }
d题大意:有n(n<=50)个靶子,每个靶子沿着一定的轨迹进行移动,移动时间为10s,求最多能同时射中几个靶子。射击时间貌似不一定是整数,看来代码可能有问题= =
假定是整数时刻射击,则枚举每秒钟每个靶子的位置,然后计算最多有几个靶子在同一直线上即可,这里暴力处理。
代码如下:
- #include
- #include
- #include
- #include
- using namespace std;
- const int maxn = 50 ;
- int n , x[maxn] , y[maxn] , a[maxn] , b[maxn] ;
- double increa[maxn] ,increb[maxn] , xx[maxn] , yy[maxn] ;
- bool vis[maxn][maxn] ;
- inline int get_max(int mm,int nn) { return mm > nn ? mm : nn ; }
- int dosth()
- {
- memset(vis,0,sizeof(vis));
- set<int> st;
- int i , j , k , ans ;
- for ( i = 0 , ans = 1 ; i < n ; i++)
- {
- for ( j = 0 ; j < n ; j++)
- {
- if( i != j && !vis[i][j] )
- {
- st.clear();
- st.insert(i);
- st.insert(j);
- for ( k = 0 ; k < n ; k++) if( k != i && k != j )
- {
- if( ( yy[j] - yy[i] ) * ( xx[k] - xx[j] ) == ( yy[k] - yy[j] ) * ( xx[j] - xx[i] ) )
- {
- st.insert(k);
- }
- }
- ans = get_max(ans,st.size());
- set<int>::iterator ite1,ite2 ;
- for ( ite1 = st.begin() ; ite1 != st.end() ; ite1++)
- {
- ite2 = ite1 ;
- ite2++;
- for ( ; ite2 != st.end() ; ite2++)
- {
- if( *ite1 != *ite2 )
- {
- vis[*ite1][*ite2] = vis[*ite2][*ite1] = 1 ;
- }
- }
- }
- }
- }
- }
- return ans ;
- }
- void solve()
- {
- int i , j , ans ;
- for( i = 0 ; i < n ; i++) { increa[i] = 0.1 * a[i] ; increb[i] = 0.1 * b[i] ; }
- for ( i = 0 , ans = 1 ; i <= 10 ; i++)
- {
- for( j = 0 ; j < n ; j++) { xx[j] = increa[j] * i + x[j] ; yy[j] = increb[j] * i + y[j] ; }
- ans = get_max(ans,dosth());
- }
- printf("%d\n",ans);
- }
- int main()
- {
- int i ;
- while (~scanf("%d",&n))
- {
- for( i = 0 ; i < n ; i++) scanf("%d%d%d%d",&x[i],&y[i],&a[i],&b[i]);
- solve();
- }
- }
水平有限,不能保证分析和代码都正确。