算法题解答
算法分析与设计 题目解答。题目在http://47.99.179.148
文章目录
分治
test_6
#include <iostream>
using namespace std;
//堆排序用数组存的方式
void minHeapSort(int a[], int current, int high){
//current是当下需要调整的节点,high为数组边界
int i = current;
int j = i*2;
while(j <= high){
if(a[j+1] < a[j] && j+1 <= high){//先是同层级比较
j = j + 1;
}
if(a[j] < a[i]){//若儿子比父节点小,则交换
swap(a[i],a[j]);
i = j;
j = 2*i;
} else{//否则跳出循环,不动
break;
}
}
}
int main() {
int n;
cin >> n;
int q[n][1000];
unsigned char c;
//输入
c = getchar();
for(int i=0; i<n; i++){
int j = 0;
while((c=getchar()) != '\n'){
if(c != ' '){
ungetc(c,stdin);//如果c不是空格要把c还回到流中
cin >> q[i][j++];
}
}
}
//应用堆排序
for (int i = 0; i < n; ++i) {
for (int j = q[i][0]/2; j >= 1 ; j--) {//自下向上,自右向左调整顺序
minHeapSort(q[i],j,q[i][0]);
}
}
//输出
for(int i=0; i<n; i++){
for(int j=1; j<=q[i][0]; j++){
cout << q[i][j] << " ";
}
cout << endl;
}
return 0;
}
test_11,最近点对
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int M,N;
struct Point{
int x;
int y;
};
Point p[50001];
//输入函数
void input(){
scanf("%d",&N);
for(int i = 0; i < N; i++){
Point point{};
scanf("%d %d",&point.x,&point.y);
p[i] = point;
}
}
//对点排序,先按照横坐标排序,横坐标相同就按照纵坐标排序
bool cmp(const Point& p1,const Point& p2){
return p1.x == p2.x ? p1.y < p2.y : p1.x < p2.x;
}
//内联函数获取两点间距
inline double dis(const Point& p1, const Point& p2){
return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
//求解最近点对
double divide(int left, int right, Point* pPoint){
Point point[50001];
if(left == right) return 0;
//分治
int mid = (left+right)/2;
Point p1{};
p1 = pPoint[mid];
//求解左边点集合和右边点集的最小距离,并再取最小
double d = min(divide(1, mid, pPoint), divide(mid + 1, right, pPoint));
int i = 1, j = mid+1, k = 0;
//将左右两边的点根据横坐标再纵坐标排序合并入临时点数组中
while (i <= mid && j <= right){
if(pPoint[i].y < pPoint[j].y) point[k++] = pPoint[i++];
else point[k++] = pPoint[j++];
}
while (i <= mid){
point[k++] = pPoint[i++];
}
while (j <= right){
point[k++] = pPoint[j++];
}
//讨论一点在左半边一点在右半边的情况
k = 0;
for(i = 1; i<=right; i++){
if(fabs(p1.x-point[i-1].x)<d) point[k++] = point[i-1];
}
for(i=0;i<k;i++){
for(j=i+1; point[j].y-point[i].y<d && j<k; j++){
d = min(d,dis(point[i],point[j]));
}
}
return d;
}
int main() {
cin >> M;
for(int i = 0 ; i < M; i++){
//输入
input();
//排序
sort(p,p+N,cmp);
//数组最小距离
double d = divide(0,N-1,p);
printf("%.2lf\n",d);
}
//for_each(points.begin(),points.end(), showMinDistance);//first和last都是迭代器对象,至于第三个参数会自己取内容传入
/*
for(vector<Point>::iterator i = points.begin();i != points.end();i++){//迭代器本质上是指向向量中对象的指针
cout << i->x << i->y << endl;
}
*/
return 0;
}
test_13
#include <iostream>
using namespace std;
int N;
long long Count = 0;
int a[50001],b[50001];
void input(){
scanf("%d", &N);
for(int i=0;i<N;i++){
scanf("%d", &a[i]);
}
}
//用归并排序(分治)
void solve(int left, int right){
if(left >= right) return;
int mid = (left + right)/2;
solve(left,mid);
solve(mid+1,right);
int i=left, j=mid+1, k=0;
while (i<=mid && j<=right){
if(a[i] > a[j]) {
b[k++] = a[j++];
Count += mid-i+1;
} else{
b[k++] = a[i++];
}
}
while (i <= mid){
b[k++] = a[i++];
}
while (j<= right){
b[k++] = a[j++];
}
for(int i=left,j=0; i<=right; i++,j++){
a[i] = b[j];
}
}
int main() {
int M;
cin >> M;
for(int i=0; i<M; i++){
input();
solve(0,N-1);
cout << Count << endl;
Count = 0;
}
return 0;
}
test_13,归并排序
#include <iostream>
using namespace std;
int N;
long long Count = 0;
int a[50001],b[50001];
void input(){
scanf("%d", &N);
for(int i=0;i<N;i++){
scanf("%d", &a[i]);
}
}
//用归并排序(分治)
void solve(int left, int right){
if(left >= right) return;
int mid = (left + right)/2;
solve(left,mid);
solve(mid+1,right);
int i=left, j=mid+1, k=0;
while (i<=mid && j<=right){
if(a[i] > a[j]) {
b[k++] = a[j++];
Count += mid-i+1;
} else{
b[k++] = a[i++];
}
}
while (i <= mid){
b[k++] = a[i++];
}
while (j<= right){
b[k++] = a[j++];
}
for(int i=left,j=0; i<=right; i++,j++){
a[i] = b[j];
}
}
int main() {
int M;
cin >> M;
for(int i=0; i<M; i++){
input();
solve(0,N-1);
cout << Count << endl;
Count = 0;
}
return 0;
}
test_16
#include <iostream>
#include <algorithm>
using namespace std;
int N,X;
int a[50001];
void input(){
scanf("%d %d",&N,&X);
for(int i=0; i<N; i++){
scanf("%d",&a[i]);
}
}
bool binary_search(int x, int low, int high){
if(low > high){//出口设置要谨慎
return false;
}
int mid = (low + high)/2;
if(a[mid] == x){
return true;
} else if(a[mid] < x){
binary_search(x,mid+1,high);
} else {
binary_search(x,low,mid-1);
}
}
void solve(){
int m,count = 0;
for(int i=0; i<N; i++){
m = X - a[i];
if(binary_search(m,0,N-1)){
count++;
}
}
if(count !=0){
cout << "yes" << endl;
} else{
cout << "no" << endl;
}
}
int main() {
int M;
cin >> M;
for(int i=0; i<M; i++){
input();
sort(a,a+N);
solve();
}
return 0;
}
动态规划
test_7
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main() {
int n;//记录组数
cin >> n;
int Input[3];
int Answer[n];
char str[41];
int dp[41][7];//用矩阵存放乘积中间结果
int num[41][41];
//格式输入
unsigned char c;
c = getchar();
for (int t = 0; t < n; ++t) {
int j = 0;
//分割字符,控制输入
while ((c=getchar())!='\n'){
if(c != ' '){
ungetc(c,stdin);
cin >> Input[j++];
}
}
//开始处理
//数字入num
for(int i=0;i<Input[0];i++){
itoa(Input[2],str,10);
int temp = 0;
for(int j=i;j<Input[0];j++){
temp = temp*10 + str[j] - '0';
num[i][j] = temp;
}
}
//用0初始化
memset(dp, 0, sizeof(dp));
memset(num, 0, sizeof(num));
//当不插入乘号后,最大乘积为本身
for (int i = 0; i < Input[0]; ++i) {
dp[i][0] = num[0][i];
}
//动态转移方程,更新解
for (int i = 0; i < Input[0]; ++i) {
for (int k = 1; k <= Input[1]; ++k) {
for (int l = 0; l < i; ++l) {
dp[i][k] = max(dp[l][k-1]*num[l+1][i],dp[i][j]);
}
}
}
Answer[t] = dp[Input[0]-1][Input[1]];
}
for (int i = 0; i < n; ++i) {
cout << Answer[i] << endl;
}
return 0;
}
test_14
#include <iostream>
using namespace std;
int N;
int maxSum = 0;
int a[50001];
void input(){
scanf("%d", &N);
for(int i=0;i<N;i++){
scanf("%d", &a[i]);
}
}
void solve(){
int dp[N];
//初始化
dp[0] = a[0];
maxSum = a[0];
for(int i=1; i<N; i++){
if(dp[i-1] < 0){
dp[i] = a[i];//如果之前的加和结果已然为-,评价为应该从头开始
} else{
dp[i] = dp[i-1] + a[i];
maxSum = max(dp[i],maxSum);
}
}
}
int main() {
int M;
cin >> M;
for(int i=0; i<M; i++){
input();
solve();
cout << maxSum << endl;
maxSum = 0;
}
return 0;
}
背包问题
test_18,背包
#include <iostream>
using namespace std;
int INT_MIN = -100;
int main(){
int m,n,C;
cin>>m;
while(m--){
cin>> n >> C;//n是宝石数目,c是背包容量
int w[n+1],v[n+1],dp[C+1];//w是重量,v是价值
dp[0]=0;
for(int i=1;i<=C;i++)
dp[i]=INT_MIN;
for(int i=1;i<=n;i++){
cin>>w[i]>>v[i];
}
//动态规划问题
for(int i=1; i<=n; i++)
for(int j=C; j>=w[i]; j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
if(dp[C]<0)
dp[C]=0;
cout << dp[C] << endl;
}
return 0;
}
特别的
test_24
#include <iostream>
#include <iomanip>
using namespace std;
#define INF 0X3f3f3f3f
int key[500];
double p[501], q[501], c[501][501], w[501][501];
void BST(double p[], double q[], int n){
for(int i=0; i<=n; i++){
c[i][i] = 0;//c(i,j)表示第i+1到j个key构造的最优二叉树的代价(平均搜索长度asl,找到和没找到要加起来),则c(0,n)是最后结果
w[i][i] = q[i];//w(i,j)表示第i+1到j个key权值及第i到j个空隙权值(空隙权值就是关键字外的搜索概率)和
}
for(int x=1; x<=n; x++){
for(int i=0; i<=n-x; i++){
int j = i + x;//不断以i到j为内容建树(迭代)
c[i][j] = INF;
w[i][j] = w[i][j-1] + p[j] + q[j];
for(int k=i+1; k<=j; k++){//以k为根的最优二叉树代价
c[i][j] = min(c[i][j],w[i][j]+c[k][j]+c[i][k-1]);
}
}
}
}
int main() {
int M, N;
cin >> M;
for(int i=0; i<M; i++){
cin >> N;
for(int j = 0; j<N; j++) cin >> key[j];//N个从小到大排好的数
for(int j = 1; j<=N; j++) cin >> p[j];//N个关键字搜索概率
for(int j = 0; j<=N; j++) cin >> q[j];//搜索关键字外的概率
BST(p,q,N);
//最终结果是从c[0][N],前面c[i][j]是不断建树记录的过程
cout << fixed<<setprecision(6)<< c[0][N] << endl;
}
return 0;
}
test_25
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
int A[10001], dp[10001], N; // 序列和, dp数组, 数字总数
void input(){
scanf("%d", &N);
for(int i=0; i<N; i++){
scanf("%d", &A[i]);
}
}
void solve(){
int ans = -1; // 记录最大的 dp[i]
for(int i = 0; i < N; i++) {
dp[i] = 1; // 至少能够自身成为一条 LIS
for(int j = 0; j < i; j++){ // 只需要递归到小于 i
// 如果不比之前的小,而且新形成的子序列更大,则接到这个人后面来
if(A[i] >= A[j] && (dp[j] + 1 > dp[i])) {
dp[i] = dp[j] + 1;
}
}
ans = max(ans, dp[i]); // 确定当前结束的最长子序列的长度之后,比较
}
printf("%d", ans);
}
int main() {
int M;
cin >> M;
for(int i=0; i<M; i++){
input();
solve();
}
return 0;
}
test_25自己解答
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;
int N;
int a[10001], ct[10001];
stack<int> a_stack;
void input(){
scanf("%d", &N);
for(int i=0; i<N; i++){
scanf("%d", &a[i]);
}
}
int clear_stack(){
int c = 0;
while (!a_stack.empty()){
a_stack.pop();
c++;
}
return c;
}
void solve(){
for(int i=0; i<N; i++){
a_stack.push(a[i]);
int s_stack_t = a[i];
for(int j=i+1; j<N; j++){
if(a[j] >= a_stack.top()){
s_stack_t = a_stack.top();
a_stack.push(a[j]);
}
if(a[j] < a_stack.top() && a[j] >= s_stack_t){//为求最长子序列,入栈的元素曲线要尽可能平滑
a_stack.pop();
s_stack_t = a_stack.top();
a_stack.push(a[j]);
}
}
ct[i] = clear_stack();
}
}
void output(){
sort(ct,ct+N);
cout << ct[N-1] << endl;
}
int main() {
int M;
cin >> M;
for(int i=0; i<M; i++){
input();
solve();
output();
}
return 0;
}