欢迎指正!!!谢谢大噶!!!
分治策略
输油管道问题
【题目描述】
某石油公司计划建造一条由西向东的主输油管道,该管道要穿过一个有n口油井的油田。从每口油田都要有一条输油管道沿最短路径(或南或北)与主管道相连。如果给定n口油井的位置,即它们的x坐标(东西向)和y坐标(南北向),应如何确定主管道的最优位置,即使各油井到主管道之间的输油管长度总和最小的位置?
【输入】
第一行是一个整数n,表示油井数量(1-1000之间),接下来n行是油井的位置,每行两个整数x和y。
【输出】
各油井到主管道之间的输油管道最小长度总和。
【输入样例】
5
1 2
2 2
1 3
3 -2
3 3
【输出样例】
6
由于要求采用分治算法,所以只好在排序的时候自己写一个快速排序。
#include <iostream>
#include <algorithm>
using namespace std;
int Partition(int a[],int left,int right)
{
int key=a[left];
while(left<right)
{
while(left<right&&a[right]>=key) //从最右边寻找,当找到比key小的值时退出循环
right--;
if(left<right)
a[left]=a[right];
while(left<right&&a[left]<=key) //从最左边开始寻找,当找到比key大的值退出循环
left++;
if(left<right)
a[right]=a[left];
}
a[left]=key;
return left;
}
void Quicksort(int a[],int left,int right) //快速排序
{
if(left<right)
{
int q=Partition(a,left,right);
Quicksort(a,left,q-1);
Quicksort(a,q+1,right);
}
}
int main()
{
int n, bestd = 0, Y; //n为管道长度,bestd为各油井到主管道之间的输油管长度最小总和,Y为主管道位置
cin >> n;
int x[n], y[n];
for (int i = 0; i < n; i++)
cin >> x[i] >> y[i];
Quicksort(y, 0, n - 1);
if (n % 2==0) //n为偶数,这里的Y不必特地求出准确的小数值,由于在主管道的南北方有相同数量的油井,主管道只需要在中间两个数之间即可,距离之和是不会变的。
Y = (y[n/2-1]+y[n/2])/2;
else //n为奇数数
Y = y[n / 2];
for (int i = 0; i < n;i++)
bestd += abs(y[i] - Y);
cout << "主管道最优的位置应该为:直线y=" << Y << endl;
cout << "各油井到主管道之间的输油管长度最小总和为:" << bestd << endl;
return 0;
}
动态规划
4、台阶问题
问题描述:有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。
经典斐波那契数列。
#include <iostream>
using namespace std;
int climbStairs(int n)
{
int a[n+1]; //a[i]表示走到第i阶楼梯的方法数量
if(n==1)
return 1;
if(n==2)
return 2;
a[1]=1;
a[2]=2;
for(int i=3;i<=n;i++)
a[i]=a[i-1]+a[i-2];
return a[n];
}
int main()
{
int n;
cin>>n;
cout<<climbStairs(n)<<endl;
return 0;
}
实际情况:给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径1,3,1,0,6,1,0就是最小路径和,返回12.
1 3 5 9 8 1 3 4 5 0 6 1 8 8 4 0
代码如下
#include <iostream>
using namespace std;
#define N 100
#define Dage 10000
int map[N+1][N+1]={0};
int dp[N+1][N+1]={0};
int flag[N + 1][N + 1]={0};
int main()
{
int n,m;
cin >> n >> m;
for (int i = 1; i <= n;i++)
for (int j = 1; j <= m;j++)
cin >> map[i][j];
for (int i = n; i >= 1; i--)
for (int j = m; j >= 1;j--)
{
if(i==n)
{
dp[i][j] = dp[i][j+1] + map[i][j];//最后一行只能往右走
flag[i][j] = 1; //1表示向右走
}
else
{
if(j==m) //最右边的点只能向下走
{
dp[i][j]=dp[i+1][j]+map[i][j];
flag[i][j] = 0; //0表示向下走
}
else
{
if(dp[i+1][j]>dp[i][j+1])
{
dp[i][j] = dp[i][j + 1]+map[i][j];
flag[i][j] = 1;
}
else
{
dp[i][j] = dp[i + 1][j]+map[i][j];
flag[i][j] = 0;
}
}
}
}
cout << dp[1][1] << endl;
int i = 1, j = 1, x = dp[i][j];
while(i<=n&&j<=m)
{
cout << "(" << i << "," << j << ")";
if(flag[i][j]==0) //0表示向下走
i = i + 1;
else //1表示向右走
j = j + 1;
}
return 0;
}
/*
测试数据:
4 4
1 1 1 1
9 9 9 1
9 9 9 1
9 9 9 0
4 4
1 9 9 9
1 9 9 9
1 9 9 9
1 1 1 0
4 4
1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
*/
贪心算法
文件连接问题:给定一个大小为n的数组F,数组元素F[i]表示第i个文件的长度。现在需要将所有文件合并成一个文件,文件越长后面连接成新文件花费的时间越长,试给出贪心算法给出文件连接顺序,保证连接文件花费的时间最短。
这题应该就是哈夫曼树的思想吧。
#include <iostream>
#include <algorithm>
using namespace std;
#define N 10086
int main()
{
int n, F[100],sum=0;
cin >> n;
for (int i = 0; i < n;i++)
scanf("%d", &F[i]);
sort(F , F + n );
for (int i = 0; i < n-1;i++)
{
sort(F + i, F + n);
F[i + 1] = F[i] + F[i + 1];
sum += F[i + 1];
}
cout <<"连接文件的最小花费为:" <<sum << endl;
system("pause");
return 0;
}
/*
数据测试:
5
4 3 1 2 5
*/
回溯算法
排列树问题吧应该!
#include <iostream>
#include <math.h>
using namespace std;
#define N 100
int a[N+1];
int n;
int count=0;
bool SushuCheak(int t)
{
int sum = a[t] + a[t - 1];
int k = sqrt(sum);
for (int i = 2; i <= k;i++)
{
if(sum%i==0)
return false;
}
return true;
}
void Backtrack(int t)
{
if(t>n)
{
int sum = a[n] + a[1];
int k = sqrt(sum);
int flag = 1;
for (int i = 2; i <= k;i++)
{
if(sum%i==0)
{
flag = 0;
break;
}
}
if(flag==1)
{
count++;
for (int i = 1; i <= n; i++)
cout << a[i] << " ";
cout<<endl;
}
}
else
{
for (int i = t; i <= n;i++)
{
swap(a[i], a[t]);
if(SushuCheak(t))
Backtrack(t + 1);
swap(a[i], a[t]);
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n;i++)
a[i] = i;
Backtrack(2);
cout<<"总共有"<<count<<"种环"<<endl;
return 0;
}