文章目录
ICPC模拟选拔 Greater New York Regional 2009 (补题)
该题解参考并转自https://blog.csdn.net/u013050857/article/details/45080467
C:poj3783
题意:
给定 B (B <= 50) 个一样的球,从 M (M <= 1000) 层楼上一个一个往下扔,存在某个楼层 K ,使得低于它的楼层往下扔球,球不会碎,在第 K 层扔下去会碎。求最坏情况下,需要扔几次才能确定这个 K 。
D : poj3748
题意:
给一个序列,然后每到奇数项的时候就输出前面的数的中位数
思路:
1、可以选择暴力,用vector和sort暴力跑一遍,比赛完看别人代码发现这样确实是可行的,我粗略计算了一下,大致是O( n2 logn)的复杂度,具体还是看sort会不会退化成O(n).
2、正解:使用树状数组 + 二分进行计算,我们再开一个数组,复制的是原来输入的数字,然后从小到大排序,然后枚举原数组的每一个数字,将对应下标插入到树状数组中,这样就相当于边插入边排序,也就是说,当我枚举并插入当前数字时,一定是将它以及它之前的数字排好序了,并且那个排好序的数组就充当索引,然后当到奇数项时,我们再利用树状数组求和的性质,二分求出1-k的k保证前k项的和为 i / 2 + 1,那么就说明k这个数是中位数 时间复杂度是O(2 * n * logn).
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
int a[maxn],tr[maxn],te[maxn];
int n,cas;
int lowbit(int x)
{
return x & (-x);
}
void add(int x,int val)
{
for(int i = x; i <= n; i += lowbit(i)) tr[i] += val;
}
int query(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
return res;
}
int Find(int x)
{
int l = 1,r = n,res;
while(l <= r){
int mid = (l + r) >> 1;
if(te[mid] >= x){
r = mid - 1;
res = mid;
}
else l = mid + 1;
}
return res;
}
int main()
{
std::ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
cin>>cas>>n;
cout<<cas<<' '<<n / 2 + 1<<endl;
memset(a,0,sizeof 0);
memset(tr,0,sizeof tr);
for(int i = 1; i <= n; i++){
cin>>a[i];
te[i] = a[i];
}
sort(te + 1,te + n + 1);
int count = 0,tot = n / 2 + 1,pos;
for(int i = 1; i <= n; i++){
pos = Find(a[i]);
//相当于用树状数组维护当前排列好的数字
add(pos,1);
if(i % 2){
count++;
int l = 1, r = n,num = i / 2 + 1,pos;
while(l <= r){
int mid = (l + r)>>1;
if(query(mid) >= num){
pos = mid;
r = mid -1;
}
else l = mid + 1;
}
cout<<te[pos];
if(count % 10 == 0) cout<<endl;
else if(count != tot) cout<<' ';
}
}
cout<<endl;
}
return 0;
}
F :
题意:
求长度为n的二进制整数中,相邻两个1的对数有k对(可重复使用)的整数个数,题目描述的很清楚了
思路:
令长度为n,相邻1的对数为k的数的个数为DP[n][k],其中以0结尾的为DP[n][k][0],以1结尾的为DP[n][k][1],那么 DP[n][k] = DP[n][k][0] + DP[n][k][1];
并且有如下状态转移方程:
- 长度为n-1的二进制数在末尾加上一个0,相邻1的对数不变,所以有:
DP[n][k][0] = DP[n-1][k][0] + DP[n-1][k][1]; - 长度为n-1的二进制数在末尾加上一个1,相邻1的对数取决于,第n-1位是0还是1,当第n-1位是1,相邻1的对数+1;当第n-1位是0,相邻1的对数不变,所以有:
DP[n][k][1] = DP[n-1][k][0] + DP[n-1][k-1][1];
并且初始状态下DP[1][0][0] = DP[1][0][1] = 1//也各算一种情况
代码
/*
dp三维数组,dp[i][j][k]:n,k,(0,1)
n位数,值位k,末尾为(0,1)
状态转移方程:
dp[i][j][0]+=(dp[i-1][j][0]+dp[i-1][j][1]);
dp[i][j][1]+=(dp[i-1][j][0]+dp[i-1][j-1][1]);
*/
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
const int maxn = 1000 + 5;
const int Mod = 998244353;
int dp[maxn][maxn][2];
int Dp(int n, int k)
{
memset(dp,0,sizeof dp);
dp[1][0][0] = dp[1][0][1] = 1;
for(int i = 1; i <= n; i++){
for(int j = 0; j <= k; j++){
dp[i][j][0] += dp[i - 1][j][0] + dp[i - 1][j][1];
dp[i][j][1] += dp[i - 1][j][0] + dp[i - 1][j - 1][1];
}
}
return dp[n][k][0] + dp[n][k][1];
}
int main()
{
int t;
cin>>t;
while(t--){
int cas,n,k;
cin>>cas>>n>>k;
int res = Dp(n,k);
cout<<cas<<' '<<res<<endl;
}
return 0;
}