华中农业大学算法实验课答案

华中农业大学算法实验课答案

  • 贪心
      • 最优分解问题:
      • 会场安排问题
      • 最小硬币数目问题
      • 硬币找钱问题
      • 汽车加油问题
      • 程序存储问题
  • 动态规划
      • 最大连续子段和
      • 最短下降路径问题:
      • 最少硬币问题:
      • 最长公共子序列
      • 矩阵链相乘
      • 所有点对的最短路径
      • 独立任务最优调度问题(动态规划)
      • 编辑距离
    • 附加
      • 双调旅行售货员问题----动态规划
      • 汽车加油行驶问题---动态规划
  • 回溯
      • 复原ip地址
  • 分治
      • 整数因子分解问题
      • 邮局选址问题
      • 集合划分问题
      • 输油管道问题
      • 寻找第k小元素
  • 分支限界法
      • 单源最短路径(迪杰斯特拉)
      • 布线问题(广搜)
      • 最小机器重量设置

贪心

最优分解问题:

问题描述:
  设n 是一个正整数。现在要求将n 分解为若干个互不相同的自然数的和,且使这些自然数的乘积最大。
编程任务:
  对于给定的正整数n,编程计算最优分解方案。
数据输入:
  由文件input.txt 提供输入数据。文件的第1 行是正整数n。
结果输出:
  程序运行结束时,将计算出的最大乘积输出到文件output.txt 中。
输入文件示例 输出文件示例

input.txt output.txt

10 30

#include 
#define MAX 100 
int main(){
	int n;
	int a[MAX];
	printf("请输入正整数n:");
	scanf("%d",&n);
	int x=2;
	int index=0;
	a[index++]=x;
	n-=x;
	//将n分解放进数组
	while(n>a[index-1]){
		a[index]=a[index-1]+1;
		n-=a[index];
		index++;
	} 
	int num=index-1;
	//将余量加入从后向前分成1加入数组
	while(n){
		a[num]++; 
		num=(num-1+index)%(index); 
		n--;
	} 
	
	//计算最大乘积 
	int result=1;
	for(int i=0;i<index;i++){
		result*=a[i];
	 } 
	 printf("\n\n最大乘积为:%d\n",result);
	
	return 0;
 }

会场安排问题

问题描述:

假设要在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的贪心算法进行安排。(这个问题实际上是著名的图着色问题。若将每一个活动作为图的一个顶点,不相容活动间用边相连。使相邻顶点着有不同颜色的最小着色数,相应于要找的最小会场数。)

编程任务:

对于给定的k 个待安排的活动,编程计算使用最少会场的时间表。

数据输入:

由文件input.txt 给出输入数据。第一行有1 个正整数k,表示有k 个待安排的活动。接下来的k 行中,每行有2 个正整数,分别表示k 个待安排的活动开始时间和结束时间。时间以0 点开始的分钟计。

结果输出:

将编程计算出的最少会场数输出到文件output.txt 。

输入文件示例输出文件示例

input.txt output.txt

5 3

1 23

12 28

25 35

27 80

36 50

//#include
//using namespace std;
//int n,ans;
//const int N=1e3+10;
//struct node
//{
//	int st,ed;
//}s[N];
//bool cmp(node x,node y)
//{
//	if(x.ed==y.ed)
//		return x.st>y.st;
//	else
//		return x.ed
//}
//int main()
//{
//	cin>>n;
//	for(int i=1,a,b;i<=n;i++)
//	{
//		cin>>a>>b;
//		s[i].st=a; s[i].ed=b;
//	}
//	sort(s+1,s+n+1,cmp);
//	int end=s[1].ed;
//	for(int i=2;i<=n;i++)
//	{
//		if(s[i].st
//		{
//			ans++;
//			end=s[i].ed;
//		}
//	}
//	cout<
//	return 0;
//} 
#include
using namespace std;
 
struct stu {
	int begin,end;
	bool flag;//用作标记
};
 
bool cmp(stu a,stu b) {
	if(a.begin==b.begin)
		return a.end<b.end;
	return a.begin<b.begin;
}
 
int fun(int k,stu a[]) {
	int cnt=k,num=0,time=0;//分别是剩余未安排活动、使用会场个数、上一个活动结束时间
	while(cnt>0) {
		for(int i=0; i<k; i++) {
			if(a[i].begin>=time&&a[i].flag==0) {
				time=a[i].end;
				a[i].flag=1;
				cnt--;
			}
		}
//一次遍历下来之后,时间置0,数量+1
		time=0;
		num++;
	}
	return num;
}
 
int main() {
	int k;
	cin>>k;
	stu a[k];
	for(int i=0; i<k; i++) {
		cin>>a[i].begin>>a[i].end;
		a[i].flag=0;
	}
	sort(a,a+k,cmp);
	cout<<fun(k,a)<<endl;
	return 0;
}

最小硬币数目问题

#include
using namespace std;
const int V[6]={1,3,10,25};   //硬币面值
int C[6],A;
void Solve()
{
    int ans=0;         //记录硬币数量
    for(int i=3;i>=0;i--)
    {
        int t=min(A/V[i], C[i]); //A/V[i] 表示当前可以取多少V[i]面值的硬币数量
		//然后min取A/V[i] 和硬币数的最小值 就是比较可取的当前最大面值硬币数和硬币数量作比较
        A-=t*V[i];  //更新A的值
        ans+=t;  //记录硬币数量
    }
    printf("%d\n",ans); //输出硬币的总数ans
}
int main()
{
    cin>>C[0]>>C[1]>>C[2]>>C[3]>>A; // 分别输入不同面值的硬币数量
    Solve();
    return 0;
}

硬币找钱问题

设有6 种不同面值的硬币,各硬币的面值分别为5 分,1 角,2 角,5 角,1 元,2 元。现要用这些面值的硬币来购物和找钱。购物时可以使用的各种面值的硬币个数存于数组Coins[1:6 ]中,商店里各面值的硬币有足够多。在1 次购物中希望使用最少硬币个数。

例如,1 次购物需要付款0.55 元,没有5 角的硬币,只好用2*20+10+5 共4 枚硬币来付款。如果付出1 元,找回4 角5 分,同样需要4 枚硬币。但是如果付出1.05 元(1 枚1 元和1 枚5 分),找回5 角,只需要3 枚硬币。这个方案用的硬币个数最少。

编程任务:

对于给定的各种面值的硬币个数和付款金额,编程计算使用硬币个数最少的交易方案。

数据输入:

由文件input.txt 给出输入数据。每一行有6 个整数和1 个有2 位小数的实数。分别表示

可以使用的各种面值的硬币个数和付款金额。文件以6 个0 结束。

结果输出:

将编程计算出的最少硬币个数输出到文件output.txt 。结果应分行输出,每行一个数据。如果不可能完成交易,则输出”impossible”。

输入文件示例

input.txt

2 4 2 2 1 0 0.95

2 4 2 0 1 0 0.55

0 0 0 0 0 0

输出文件示例

output.txt

2

3

#include 
const int N = 20000 ;
int ch[N]; // ch[i]为面值为i的钱至少需要的硬币个数
int dp[N]; // dp[i]为当前拥有的硬币数量条件下表示面值为i的最少硬币个数
int v[ 6 ] = { 1 , 2 , 4 , 10 , 20 , 40 }; // 每种硬币对应面值,依次为1,2,4,10,20,40个五分,即5,10,20,50,100,200;
int nm[ 6 ]; // 对应于当前拥有的每种硬币个数
void init() // 计算ch[i]
{
	int i,j;
	for (i = 0 ;i < N;i ++ )ch[i] =- 1 ;
	ch[ 0 ] = 0 ;
	for (i = 0 ;i < 6 ;i ++ )
	{
		for (j = v[i];j < N;j ++ ) 
		{
			if (ch[j - v[i]] !=- 1 )
			{
				int temp = ch[j - v[i]] + 1 ;
				if (ch[j] ==- 1 || temp < ch[j])ch[j] = temp;
			}
		}
	}
}
int main()
{
	init(); // 计算出ch[i]

	while (scanf( " %d%d%d%d%d%d " , & nm[ 0 ], & nm[ 1 ], & nm[ 2 ], & nm[ 3 ], & nm[ 4 ], & nm[ 5 ]) != EOF)
	{
		int sum = 0 ;
		int kk;
		for (kk = 0 ;kk < 6 ;kk ++ )sum += nm[kk];
		if (sum == 0 ) break ;
		double weight;
		scanf( " %lf " , & weight);
		weight = weight * 100 ;
		int w = int (weight + 0.0000001 ); // 处理精度问题	
		if (w % 5 != 0 ) // 若不能整除,则无法表示
		{
			printf( " impossible\n " );
			continue ;
		}
		else
			w = w / 5 ;
		int i,j;
		memset(dp, - 1 , sizeof (dp));
		dp[ 0 ] = 0 ;
		int bigger = 0 ;
		for (i = 0 ;i < 6 ;i ++ )//计算顾客支付面值i需要的最少硬币数dp[i]
		{
			while (nm[i] -- ) 
			{
				bigger = bigger + v[i];
				for (j = bigger;j >= v[i];j -- )
				{
					if (dp[j - v[i]] !=- 1 )
					{
						int temp = dp[j - v[i]] + 1 ;
						if (dp[j] ==- 1 || temp < dp[j])
						{
							dp[j] = temp;
						}
					}
				}
			}
		}
		int ans =- 1 ;
		for (i = w;i <= bigger;i ++ )//寻找最少硬币组合
		{
			if (dp[i] !=- 1 )
			{
				int need = i - w;
				if (ch[need] !=- 1 )
				{
					int temp = dp[i] + ch[need];
					if (ans ==- 1 || ans > temp)ans = temp;
				}
			}
		}
		if (ans !=- 1 )
			printf( " %d\n " ,ans);
		else
			printf( " impossible\n " );
	}
	return 0 ;
}

汽车加油问题

问题描述:

一辆汽车加满油后可行驶n 公里。旅途中有若干个加油站。设计一个有效算法,指出应在哪些加油站停靠加油,使沿途加油次数最少。并证明算法能产生一个最优解。

编程任务:

对于给定的n 和k 个加油站位置,编程计算最少加油次数。

数据输入:

由文件input.txt 给出输入数据。第一行有2 个正整数n 和k,表示汽车加满油后可行驶n 公里,且旅途中有k 个加油站。接下来的1 行中,有k+1 个整数,表示第k 个加油站与第k-1 个加油站之间的距离。第0 个加油站表示出发地,汽车已加满油。第k+1 个加油站表示目的地。

结果输出:

将编程计算出的最少加油次数输出到文件output.txt 。如果无法到达目的地,则输出”No Solution”。

输入文件示例输出文件示例

input.txt output.txt

7 7 4

1 2 3 4 5 1 6 6

//#include
//using namespace std;
//const int N=1e3+10;
//int a[N],n,k,b[N];
//int sum,ans,len;
//int main()
//{
//	cin>>n>>k;
//	for(int i=1;i<=k+1;i++)
//	{
//		cin>>a[i];
//		sum+=a[i];
//		b[i]=b[i-1]+a[i];
//	}
//	while(len
//	{
//		int flag=0,now=0;
//		for(int i=k+1;i>=1;i--)
//		{
//			if(b[i]-b[now]<=k && !flag)
//			{
//				flag=1;
//				ans++;
//				now=k;
//				len+=a[k];
//			}
//		}
//	}
//	cout<
//	return 0;
//}
#include 
#include 
 
int main()
{
    int m,n;
    scanf("%d%d",&m,&n);
    int i,j;
    int a[n+1];
    for(i=0;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    int sum=a[0];//从第一站记起
    int count=0;
    for(i=0;i<=n;i++)
    {
        if((sum+a[i])<=m)
        {
            sum+=a[i];
        }
        else if(a[i]>m)
        {
            printf("No Solution!");
            return 0;
        }
        else
        {
            count++;
            sum=a[i];//更新距离
        }
    }
    printf("%d\n",count);
    return 0;
}

程序存储问题

问题描述:

设有n 个程序{1,2,…, n }要存放在长度为L 的磁带上。程序i 存放在磁带上的长度是li,1≤i≤n 。程序存储问题要求确定这n 个程序在磁带上的一个存储方案,使得能够在磁带上存储尽可能多的程序。

编程任务:

对于给定的n 个程序存放在磁带上的长度,编程计算磁带上最多可以存储的程序数。

数据输入:

由文件input.txt 给出输入数据。第一行是2 个正整数,分别表示文件个数n 和磁带的长度L。接下来的1 行中,有n 个正整数,表示程序存放在磁带上的长度。

结果输出:

将编程计算出的最多可以存储的程序数输出到文件output.txt 。

输入文件示例输出文件示例

input.txt output.txt

6 50 5

2 3 13 8 80 20

#include
using namespace std;
int n,L,ans,sum;
int a[100]; 
int main()
{
	cin>>n>>L;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(sum+a[i]<=L)
		{
			ans++;
			sum+=a[i];
		}
		else break;
	}
	cout<<ans<<endl;
//	2,3,8,13,20,80
	return 0;
}

动态规划

最大连续子段和

//ans[i]=max{ans[i-1]+a[i],a[i]}
//如果: ans[i-1]+a[i]>max =>更新最大值
//如果: ans[i-1]+a[i]<0 =>ans[i]=0
#include 
using namespace std;

int max_sum(int n,int a[n]){
    int ans[100] = {0}; //答案数组
    int max=0;
    if(a[0]>0){
        ans[0]=a[0];
        max=a[0];
    }
    for(int i=1;i<n;i++){
        ans[i]=ans[i-1]+a[i];
        if(ans[i]>max){
            max=ans[i];
        }
        if(ans[i]<0){
            ans[i]=0;
        }
    }
    /*for(int i=0;i
    return max;
}

int main() {
    int n; //序列长度n
    int a[100]; //输入的整数序列
    cin>>n;
    for (int i=0; i<n; i++){ //输入待计算的n个数字
        cin>>a[i];
    }
    cout<<"最大子序列和为:"<<max_sum(n,a)<<endl;//调用max_sum函数,并且输出最大子序列和
    //cout<
}

最短下降路径问题:

【问题描述】

给定一个方形整数数组 A,我们想要得到通过 A 的下降路径的最小和。

下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列。

【输入形式】

第一行输入数组的大小n,第二行以后输入的n行分别是数组的第n行元素

【输出形式】

输出1行中含有一个数字,表示最短下降路径和。

【样例输入】

3

2 1 3

6 5 4

7 8 9
【样例输出】

13

//无优化版 
//#include
//using namespace std;
//const int N=1e3+10;
//int a[N][N],dp[N][N];
//int n,minn=N;
//int main()
//{
//	cin>>n;
//	for(int i=1;i<=n;i++)
//		for(int j=1;j<=n;j++)
//			cin>>a[i][j];
//	for(int j=1;j<=n;j++)
//		dp[1][j]=a[1][j];
//	//二维 
//	for(int i=2;i<=n;i++)
//	{
//		for(int j=1;j<=n;j++)
//		{
//			dp[i][j]=dp[i-1][j];
//			if(j>=2) dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
//			if(j<=n-1) dp[i][j]=min(dp[i][j],dp[i-1][j+1]);
//			dp[i][j]+=a[i][j];
//		}
//	}
//	for(int j=1;j<=n;j++)
//		minn=min(dp[n][j],minn);		
//	cout<
//	return 0;
//}
// 滚动数组优化问题 
#include
using namespace std;
const int N=1e3+10;
int a[N][N],dp[N];
int n,minn=N;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j];
	for(int j=1;j<=n;j++)
		dp[j]=a[1][j];
	//二维 
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			//处理边界问题 
			if(j>=2) dp[j]=min(dp[j],dp[j-1]);
			if(j<=n-1) dp[j]=min(dp[j],dp[j+1]);
			dp[j]+=a[i][j];
		}
	}
	for(int j=1;j<=n;j++)
		minn=min(dp[j],minn);		
	cout<<minn<<endl;
	return 0;
}

最少硬币问题:

问题描述:

设有n 种不同面值的硬币,各硬币的面值存于数组T[1:n ]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n ]中。对任意钱数0≤m≤20001,设计一个用最少硬币找钱m 的方法。

编程任务:

对于给定的1≤n≤10,硬币面值数组T 和可以使用的各种面值的硬币个数数组Coins, 以及钱数m,0≤m≤20001,编程计算找钱m 的最少硬币数。

数据输入:

由文件input.txt 提供输入数据,文件的第一行中只有1 个整数给出n 的值,第2 行起每行2 个数,分别是T[j] 和Coins[j] 。最后1 行是要找的钱数m。

结果输出:

程序运行结束时,将计算出的最少硬币数输出到文件output.txt 中。问题无解时输出-1。

输入文件示例输出文件示例

input.txt output.txt

3 5

1 3

2 3

5 3

18

 //无优化版多重背包 
#include
using namespace std;
const int N=1e5+10;
int dp[N],n,m;
int T[N],coin[N];
int main()
{
	cin>>n;
	int cnt=0;
	for(int i=1,a,b;i<=n;i++)
	{
		cin>>a>>b;
		while(b--) T[++cnt]=a;	
	}
	cin>>m;
	//初始化 
	for(int j=1;j<=m;j++)  dp[j]=N;
	for(int i=1;i<=cnt;i++)
		for(int j=m;j>=T[i];j--)
			dp[j]=min(dp[j],dp[j-T[i]]+1);
	cout<<dp[m]<<endl;
	return 0;
}
// 二进制优化
 

最长公共子序列

【问题描述】给定两个字符串text1和text2,返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列,则返回0。一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。

【输入形式】输入的第1行中有一个字符串,表示text1;输入的第2行中有一个字符串,表示text2。
【输出形式】输出1行一个整数,表示text1和text2的最长公共子序列的长度。
【样例输入】

abcde

ace
【样例输出】

3
【样例说明】

最长公共子序列是ace,长度为3
【说明】

1<=text1.length, text2.length<=1000

text1和text2仅有小写英文字符组成。

#include
using namespace std;
const int N=1e4+10;
string s1,s2;
int dp[N][N];
int main()
{
	cin>>s1>>s2;
	for(int i=1;i<=(int)s1.size();i++)
	{
		for(int j=1;j<=(int)s2.size();j++)
		{
			if(s1[i-1]==s2[i-1]) dp[i][j]=dp[i-1][j-1]+1;
			else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			
		}
	}
	cout<<dp[(int)s1.size()][(int)s2.size()];
	cout<<endl;
}

矩阵链相乘

【问题描述】给定n个矩阵M1,M2…Mn,他们的维数分别是r1c1,r2c2…rn*cn,要求使用【动态规划】的策略求解矩阵连乘的最优计算代价(总乘法次数最少)。题目保证矩阵相乘一定是有效的。

例如有三个矩阵M1,M2,M3,他们的维度分别是210,102,210。按照矩阵乘法的结合律,可以先把M1和M2相乘,然后把结果和M3相乘,总的乘法次数为2102+2210=80次;也可以先把M2和M3相乘,再用M1去相乘,这种方式下总的乘法次数为10210+210*10=400次。因此最优计算代价为80。
【输入形式】输入的第1行中有1个数字n,表示矩阵的个数;接下来n行,每行2个整数ri和ci,分别表示矩阵Mi的行数和列数。
【输出形式】输出1行中有一个数字,表示n个矩阵相乘的最优计算代价。
【样例输入】

3

2 10

10 2

2 10
【样例输出】

80
【说明】

n>=2

1<=ri,ci<=20

#include
using namespace std;
const int N=1e8+1;
int a[21],c[21][21],b[21][21];
int n,ans=1;
int main()
{
	cin>>n;
	for(int i=1,l;i<=2*n;i++)
	{
		cin>>l;
		if(i%2!=0 || i==2*n)
			a[ans++]=l;
	}
	for(int d=2;d<=n;d++)
		for(int i=1;i<=n-d+1;i++)
		{
			int j=i+d-1;
			b[i][j]=N;
			for(int k=i;k<=i+d-2;k++)
			{
				int temp=b[i][k]+b[k+1][j]+a[i]*a[k+1]*a[j+1];
				if(temp<b[i][j])
				{
					b[i][j]=temp;
					c[i][j]=k;
				}
			} 
		}
	cout<<b[1][n];
	return 0;
}

所有点对的最短路径

【问题描述】给定一个非负的加权有向图G,求其中任意两个节点之间的最短路径。
【输入形式】输入的第1行包含2个整数n和m,表示图G中包含n个节点和m条边。接下来m行,每行中有3个整数i,j,w,表示从节点i到节点j存在一条边(节点编号从1开始),该边的权重为w。
【输出形式】

输出最短路径矩阵,共包含n行,每行包含n个数字(数字之间使用空格分割),表示该节点到其他节点的最短距离。

特别地,节点到自身的距离定义为0;如果节点之间无法到达,使用1e9+7表示他们之间的距离。
【样例输入】

3 5

1 2 2

1 3 9

2 1 8

2 3 6

3 1 1
【样例输出】

0 2 8

7 0 6

1 3 0
【思考】如果出现负边,如何进行改进

// 三重循环,佛洛依德 
#include
using namespace std;
int a[100][100],n,m;
const int N=1e9+7;
int main()
{
	cin>>n>>m;
	for(int i=1,x,y,w;i<=m;i++)
	{
		cin>>x>>y>>w;
		a[x][y]=w;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(!a[i][j]) a[i][j]=N;
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==j) a[i][j]=0;
			cout<<a[i][j]<<' ';
		}
		cout<<endl;	
	}		
	return 0;
}

独立任务最优调度问题(动态规划)

问题描述:
用2 台处理机A 和B 处理n 个作业。设第i 个作业交给机器A 处理时需要时间ai ,若由机器B 来处理,则需要时间bi。由于各作业的特点和机器的性能关系,很可能对于某些i, 有ai ≥ bi ,而对于某些j,j≠i,有aj < bj 。既不能将一个作业分开由2 台机器处理,也没有一台机器能同时处理2 个作业。设计一个动态规划算法,使得这2 台机器处理完这n 个作业的时间最短(从任何一台机器开工到最后一台机器停工的总时间)。研究一个实例:(a1,a2,a3,a4,a5,a6)=(2,5,7,10,5,2);(b1,b2,b3,b4,b5,b6)=(3,8,4,11,3,4) 。
编程任务
对于给定的2 台处理机A 和B 处理n 个作业,找出一个最优调度方案,使2 台机器处理完这n 个作业的时间最短。
数据输入:
由文件input.txt 提供输入数据。文件的第1 行是1 个正整数n, 表示要处理n 个作业。接下来的2 行中,每行有n 个正整数,分别表示处理机A 和B 处理第i 个作业需要的处理时间。
结果输出:
  程序运行结束时,将计算出的最短处理时间输出到文件output.txt 中。
示例
输入:
6
2 5 7 10 5 2
3 8 4 11 3 4
输出
15

#include
using namespace std;
const int N=1e5+10;
int n=0,j=0,sum=0,ans=1e5+10; 
int a[N],b[N],dp[N];
int main()
{
   cin>>n;
   for(int i = 0; i < n; i++)
   {
       cin>>a[i];
       sum = sum+a[i];
   }
   for(int i = 0; i < n; i++) cin>>b[i];
   for(j = 0; j < n; j++)
   {
       for(int i = sum; i >= 0; i--)
       {
           if(i >= a[j])  dp[i] = min(dp[i]+b[j],dp[i-a[j]]);
           else  dp[i] = dp[i]+b[j];
       }
   }
   for(int i = 0; i<= sum; i++)
   {
       j = max(i,dp[i]);
       if(ans>j)  ans = j;
   }
   cout<<ans;
   return 0;
}

编辑距离

【问题描述】

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数。

你可以对一个单词进行如下三种操作:

插入一个字符

删除一个字符

替换一个字符

【输入形式】

输入共包含两行,每行1个字符串,分别表示word1和word2。

【输出形式】

输出1个整数,表示将word1转换为word2所使用的最少操作数。
【样例输入】

horse

ros
【样例输出】

3

	// 字符桌动态规划
#include
using namespace std;
const int N=1e3+10;
string s1,s2;
int dp[N][N];
int main()
{
	cin>>s1>>s2;
	int n=(int)s1.size(),m=(int)s2.size();
	// 初始化
	for(int i=1;i<=n;i++)
		dp[i][0]=i;
	for(int j=1;j<=m;j++)
		dp[0][j]=j;
	// 递推
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(s1[i-1]==s2[j-1])	dp[i][j]=dp[i-1][j-1];
			else dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
		}
	cout<<dp[n][m]<<endl; 
	return 0;
}

附加

双调旅行售货员问题----动态规划

欧氏旅行售货员问题是对给定的平面上n 个点确定一条连接这n 个点的长度最短的哈密顿回路。由于欧氏距离满足三角不等式,所以欧氏旅行售货员问题是一个特殊的具有三角不等式性质的旅行售货员问题。它仍是一个NP 完全问题。最短双调TSP 回路是欧氏旅行售货员问题的特殊情况。平面上n 个点的双调TSP 回路是从最左点开始,严格地由左至右直到最右点,然后严格地由右至左直至最左点,且连接每一个点恰好一次的一条闭合回路。

编程任务:

给定平面上n 个点,编程计算这n 个点的最短双调TSP 回路。

数据输入:

由文件input.txt 给出输入数据。第1 行有1 个正整数n,表示给定的平面上的点数。接下来的n 行中,每行2 个实数,分别表示点的x 坐标和y 坐标。

结果输出:

将计算的最短双调TSP 回路的长度(保留2 位小数)输出到文件output.txt 。

输入文件示例 输出文件示例

input.txt output.txt

7 25.58

0 6

1 0

2 3

5 4

6 1

7 5

8 2

#include
using namespace std;
const double N = 10086;
double dis[51][51];
int n;

struct node {
    double x, y;
    bool operator<(const node& p) {
        return x < p.x || (x == p.x && y < p.y);
    }
    bool operator>(const node& p) {
        return x > p.x || (x == p.x && y > p.y);
    }
}p[51];

double dist(const node& a, const node& b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

void qsort(int l, int r) {
    if (l < r) {
        int i = l, j = r;
        node mid = p[l + (r - l) / 2];
        while (i <= j) {
            while (p[i] < mid)
                i++;
            while (p[j] > mid)
                j--;
            if (i <= j) {
                node t = p[i]; p[i] = p[j], p[j] = t;
                i++, j--;
            }
        }
        qsort(l, j);
        qsort(i, r);
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> p[i].x >> p[i].y;
    qsort(1, n);
    dis[1][2] = dist(p[1], p[2]);
    for (int j = 3; j <= n; j++) {
        for (int i = 1; i <= j - 2; ++i) {
            dis[i][j] = dis[i][j - 1] + dist(p[j - 1], p[j]);
        }
        dis[j - 1][j] = N;
        for (int k = 1; k <= j - 2; ++k) {
            double tmp = dis[k][j - 1] + dist(p[k], p[j]);
            dis[j - 1][j] = min(tmp, dis[j - 1][j]);
        }
    }
    dis[n][n] = dis[n - 1][n] + dist(p[n - 1], p[n]);
    cout << fixed << setprecision(2) << dis[n][n];
    return 0;
}

汽车加油行驶问题—动态规划

给定一个N*N 的方形网格,设其左上角为起点◎,坐标为(1,1),X 轴向右为正,Y 轴向下为正,每个方格边长为1。一辆汽车从起点◎出发驶向右下角终点▲,其坐标为(N, N)。在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

(1)汽车只能沿网格边行驶,装满油后能行驶K 条网格边。出发时汽车已装满油,在

起点与终点处不设油库。

(2)当汽车行驶经过一条网格边时,若其X 坐标或Y 坐标减小,则应付费用B,否则

免付费用。

(3)汽车在行驶过程中遇油库则应加满油并付加油费用A。

(4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)。

(5)(1)~(4) 中的各数N、K、A、B、C 均为正整数。

编程任务:

求汽车从起点出发到达终点的一条所付费用最少的行驶路线。

数据输入:

由文件input.txt 提供输入数据。文件的第一行是N,K,A,B,C 的值,2 ≤ N ≤ 100,

2 ≤ K ≤ 10。第二行起是一个N*N 的0-1 方阵,每行N 个值,至N+1 行结束。方阵的第i 行第j 列处的值为1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻的2 个数以空格分隔。

结果输出:

程序运行结束时,将找到的最优行驶路线所需的费用,即最小费用输出到文件output.txt 中。文件的第1 行中的数是最小费用值。

输入文件示例 输出文件示例

input.txt output.txt

9 3 2 3 6 12

0 0 0 0 1 0 0 0 0

0 0 0 1 0 1 1 0 0

1 0 1 0 0 0 0 1 0

0 0 0 0 0 1 0 0 1

1 0 0 1 0 0 1 0 0

0 1 0 0 0 0 0 1 0

0 0 0 0 1 0 0 0 1

1 0 0 1 0 0 0 1 0

0 1 0 0 0 0 0 0 0

#include
using namespace std;
const int N=2e5+10;
int n,k,A,B,C,a[110][110];
bool L[N],v[N];
int ft[N],length=0;
int ff[4]={-1,0,1,0},f2[4]={0,-1,0,1},q[N],tt=1,dd=2,f[N],ans=2e9+1;;
struct bian{
	int y,z,next;
};
bian e[N];
void bld(int x,int y,int z)
{
	e[++length]=(bian){y,z,ft[x]};
	ft[x]=length;
}
int pos(int x,int y)
{
	return (x-1)*n+y;
}
int main()
{
	cin>>n>>k>>A>>B>>C;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
			if(a[i][j])
			{
				for(int c=0;c<=k;c++)
					L[pos(i,j)+c*n*n]=true;	
			}
			int limit=(1-a[i][j])*(k-1);
			for(int c=0;c<=limit;c++)
			for(int l=0;l<4;l++)
			{
				int x=i+ff[l],y=j+f2[l];
				if(x<1||x>n||y<1||y>n) continue;
				bld(pos(i,j)+c*n*n,pos(x,y)+(c+1)*n*n,l<2?B:0);
			}
		}
	memset(f,63,sizeof(f));
	q[tt]=1;
	f[1]=0;
	v[1]=true;
	while(tt!=dd)
	{
		int x=q[tt++];
		v[x]=false;
		tt=tt>(k+1)*n*n?1:tt;
		if(L[x]&&x>n*n)
		{
			if(f[(x-1)%(n*n)+1]>f[x]+A)
			{
				f[(x-1)%(n*n)+1]=f[x]+A;
				if(!v[(x-1)%(n*n)+1])
				{
					v[q[dd++]=(x-1)%(n*n)+1]=true;
					dd=dd>(k+1)*n*n?1:dd;	
				}
			}
			continue;
		}
		if(!L[x]&&x>n*n)
		{
			if(f[(x-1)%(n*n)+1]>f[x]+A+C)
			{
				f[(x-1)%(n*n)+1]=f[x]+A+C;
				if(!v[(x-1)%(n*n)+1])
				{
					v[q[dd++]=(x-1)%(n*n)+1]=true;	
					dd=dd>(k+1)*n*n?1:dd;
				}
			}
		}
		for(int i=ft[x];i;i=e[i].next)
		{
			int y=e[i].y;
			if(f[y]>f[x]+e[i].z)
			{
				f[y]=f[x]+e[i].z;
				if(!v[y])v[q[dd++]=y]=true,dd=dd>(k+1)*n*n?1:dd;
			}
		}
	}
	for(int c=0;c<=k;c++)
		ans=min(ans,f[(c+1)*n*n]);
	cout<<ans;
}

回溯

复原ip地址

【问题描述】有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245"和"192.168.1.312” 是 无效 IP 地址。

给定一个只包含数字的字符串s,用以表示一个IP地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你不能重新排序或删除s中的任何数字。你可以按任何顺序返回答案。

【输入形式】输入一行包含一个字符串s,表示待复原的IP地址。
【输出形式】输出包含若干行。每行一个字符串,表示一种有效的IP地址表示方案。
【样例输入】

25525511135

【样例输出】

255.255.11.135

255.255.111.35

#include
using namespace std;
string s;
vector<string>vt;
bool isValid(string s,int start,int end)
{
	if(s[start]=='0' && start!=end) return false;
	if(start >end) return false;
	int num=0;
	for(int i=start;i<=end;i++)
	{
		if(s[i]>'9' || s[i]<'0') return false;
		num=10*num+(s[i]-'0');	
		if(num>255) return false;
	}
	return true; 
}
void dfs(string s,int start,int ans)
{
	if(ans==3)
	{
		if(isValid(s,start,s.size()-1))
			vt.push_back(s);
		return;
	}
	for(int i=start;i<(int)s.size();i++)
	{
		if(isValid(s,start,i)) // 局部判断插入点的位置是否合适 
		{
			s.insert(s.begin()+i+1,'.');
			dfs(s,i+2,ans+1);
			s.erase(s.begin()+i+1);  // 回溯 
		}
	}
}
int main()
{
	cin>>s;
	dfs(s,0,0);
//	for(auto ss:vt) cout<
	for(vector<string>::iterator i=vt.begin();i!=vt.end();i++)  
		cout<<(*i)<<endl;
	return 0;
}

分治

整数因子分解问题

问题描述:

大于1 的正整数n 可以分解为:n=x1x2…*xm。

例如,当n=12 时,共有8 种不同的分解式:

12=12;

12=6*2;

12=4*3;

12=3*4;

12=322;

12=2*6;

12=232;

12=223 。

编程任务:

对于给定的正整数n,编程计算n 共有多少种不同的分解式。

数据输入:

由文件input.txt 给出输入数据。第一行有1 个正整数n (1≤n≤2000000000)。

结果输出:

将计算出的不同的分解式数输出到文件output.txt 。

输入文件示例 输出文件示例

input.txt output.txt

12 8

#include
using namespace std;
int digui(int x)
{
    if(x<1)  return -1;
    else if (x==1) return 1;
    int sum = 0;
    for(int i=2;i<=x;i++)
        if (x%i==0)   sum+=digui(x/i);
    return sum;
}
int main()
{
    int x;
    cin>>x;
    int ans = digui(x);
    cout<<ans<<endl; 
    return 0;
}

邮局选址问题

问题描述:

在一个按照东西和南北方向划分成规整街区的城市里,n 个居民点散乱地分布在不同的街区中。用x 坐标表示东西向,用y 坐标表示南北向。各居民点的位置可以由坐标(x,y) 表示。街区中任意2 点(x1,y1) 和(x2,y2) 之间的距离可以用数值|x1-x2|+|y1-y2| 度量。

居民们希望在城市中选择建立邮局的最佳位置,使n 个居民点到邮局的距离总和最小。

编程任务:

给定n 个居民点的位置,编程计算n 个居民点到邮局的距离总和的最小值。

数据输入:

由文件input.txt 提供输入数据。文件的第1 行是居民点数n,1<=n<=10000。接下来n 行是居民点的位置,每行2 个整数x 和y,-10000<=x,y<=10000。

结果输出:

程序运行结束时,将计算结果输出到文件output.txt 中。文件的第1 行中的数是n 个居民点到邮局的距离总和的最小值。

输入文件示例 输出文件示例

input.txt output.txt

5 10

1 2

2 2

1 3

3 -2

3 3

//#include
//using namespace std;
//const int N=1e4+10;
//int n;
//int sx,sy;
//int a[N],b[N];
//int main()
//{
//	cin>>n;
//	for(int i=1,x,y;i<=n;i++)
//	{
//		cin>>x>>y;
//		a[i]=x;
//		b[i]=y;
//	}
//	sort(a+1,a+n+1);
//	sort(b+1,b+n+1);
//	sx=a[n/2]; sy=b[n/2];
//	for(int i=1;i<=n;i++)
//	{
//		sx+=abs(a[i]-sx);
//		sy+=abs(b[i]-sy);
//	}
//	cout<
//	return 0;
//} 
#include
using namespace std;
int main()
{
    int a[100],b[100];
    int n,x,y,sumx=0,sumy=0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i]>>b[i];
    }
    sort(a,a+n);
    sort(b,b+n);
    x=a[n/2];
    y=b[n/2];
    for(int j=0;j<n;j++)
    {
        sumx+=abs(a[j]-x);
        sumy+=abs(b[j]-y);
    }
    cout<<sumx+sumy<<endl;
    return 0;
}

集合划分问题

问题描述:

n 个元素的集合{1,2,., n }可以划分为若干个非空子集。例如,当n=4 时,集合{1,2, 3,4}可以划分为15 个不同的非空子集如下:

{{1},{2},{3},{4}},

{{1,2},{3},{4}},

{{1,3},{2},{4}},

{{1,4},{2},{3}},

{{2,3},{1},{4}},

{{2,4},{1},{3}},

{{3,4},{1},{2}},

{{1,2},{3,4}},

{{1,3},{2,4}},

{{1,4},{2,3}},

{{1,2,3},{4}},

{{1,2,4},{3}},

{{1,3,4},{2}},

{{2,3,4},{1}},

{{1,2,3,4}}

其中,集合{{1,2,3,4}} 由1 个子集组成;集合{{1,2},{3,4}},{{1,3},{2, 4}},{{1,4},{2,3}},{{1,2,3},{4}},{{1,2,4},{3}},{{1,3,4},{2}},{{2, 3,4},{1}} 由2 个子集组成;集合{{1,2},{3},{4}},{{1,3},{2},{4}},{{1,4}, {2},{3}},{{2,3},{1},{4}},{{2,4},{1},{3}},{{3,4},{1},{2}} 由3 个子集组成;集合{{1},{2},{3},{4}} 由4 个子集组成。

编程任务:

给定正整数n 和m,计算出n 个元素的集合{1,2,., n }可以划分为多少个不同的由m 个非空子集组成的集合。

数据输入:

由文件input.txt 提供输入数据。文件的第1 行是元素个数n 和非空子集数m。

结果输出:

程序运行结束时,将计算出的不同的由m个非空子集组成的集合数输出到文件output.txt 中。

输入文件示例 输出文件示例

input.txt output.txt

4 3 6

#include
using namespace std;

int fun(int n,int m) {
	if(m==1||m==n)
		return 1;
	else
		return fun(n-1,m-1)+m*fun(n-1,m);
}

int main() {
	int n,m;
	cin>>n>>m;
	cout<<fun(n,m)<<endl;
	return 0;
}

输油管道问题

问题描述:

某石油公司计划建造一条由东向西的主输油管道。该管道要穿过一个有n 口油井的油田。从每口油井都要有一条输油管道沿最短路经(或南或北)与主管道相连。如果给定n 口油井的位置,即它们的x 坐标(东西向)和y 坐标(南北向),应如何确定主管道的最优位置, 即使各油井到主管道之间的输油管道长度总和最小的位置?证明可在线性时间内确定主管道的最优位置。

编程任务:

给定n 口油井的位置,编程计算各油井到主管道之间的输油管道最小长度总和。

数据输入:

由文件input.txt 提供输入数据。文件的第1 行是油井数n,1<=n10000。接下来n 行是油井的位置,每行2 个整数x 和y,-10000<=x,y<=10000 。

结果输出:

程序运行结束时,将计算结果输出到文件output.txt 中。文件的第1 行中的数是油井到主管道之间的输油管道最小长度总和。

输入文件示例 输出文件示例

input.txt output.txt

5 6

1 2

2 2

1 3

3 -2

3 3

#include
using namespace std;
int n,x,a[1000];     
int main()
{
	cin>>n;
    for(int k=0;k<n;k++)
     	cin>>x>>a[k];
    sort(a,a+n);    
    int min=0;
    for(int i=0;i<n;i++)
     min += (int)fabs(a[i]-a[n/2]);
    cout<<min<<endl;
}

寻找第k小元素

【问题描述】给定一个长度为n的整数数组nums和整数k,输出数组中的第k小元素。要求不能对数组排序,使用分治的思想求解。

【输入形式】输入的第1行中有1个数字n,表示数组的长度;第2行中有n个数字,表示数组的元素;第3行中有1个数字k。

【输出形式】输出1行中有1个数字,表示数组中的第k小元素。
【样例输入】

6

3 2 1 4 6 5

2
【样例输出】

2
【说明】
1<=k<=n<=10^4

10-5<=nums[i]<=105

#include
using namespace std;
const int N=1e3+10;
int a[N];
int n,k,minn;
int select(int a[],int low,int high,int k)
{
	int p=high-low-1;
	int q; //子数组个数
	if(p<44)
	{
		sort(a+low,a+low+high+1);
		return a[low+k-1];//返回	
	}
	else
	{
		q=p/5;
		//找出每个数组的中间项
		int m[N],i=0,j=0;
		while(i+4<=high)
		{
			m[j++]=select(a,i,i+4,3);
			i+=5;
		}
		//找m数组的中间项
		int mm= select(m,0,q-1,(q+1)/2);
		//将数组分为三组
		int a1[N],a2[N],a3[N],x=0,y=0,z=0;
		for(int i=low;i<p;i++)
		{
			if(a[i]<mm) a1[x++]=a[i];
			else if(a[i]==mm) a2[y++]=a[i];
			else  a3[z++]=a[i];	
		} 
		//分类讨论即可
		if(x>=k) return select(a1,0,x-1,k);
		else if(x+y>=k) return mm;
		else return select(a3,0,z-1,k-x-y); 
	}	
}
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	cin>>k;
	int minn=select(a,0,n-1,k);
	cout<<minn<<endl;
} 
//3 -10 2 -1 8 5 

分支限界法

单源最短路径(迪杰斯特拉)

【问题描述】

给定带权有向图G =(V,E),其中每条边的权是非负实数。另外,还给定V中的一个顶点,称为源(默认源点为顶点1)。现在要计算从源到所有其他各顶点的最短路长度。这里路的长度是指路上各边权之和。这个问题通常称为单源最短路径问题。

【输入形式】

第一行是顶点数n和边数m

随后n行是顶点编号

随后m行是所有边的起点 终点和边长

【输出形式】

输出n-1行,分别为源点到其他各点的最短路径

【样例输入】

5 6

1

2

3

4

5

1 2 5

2 5 1

1 3 2

3 4 3

4 5 2

1 4 1

【样例输出】

5

2

1

3

#include
using namespace std;
#define N 9999999
//邻接矩阵:顶点集,边集,顶点数,边数,可以采用结构体,也可不用 
int a[55][55],d[55],visit[55];//三个数组,d存储最短路径,a建立邻接矩阵,visit存放已经访问过的节点
int path[55]; //显示路径    
int main()
{
    int n,m,s=0,i,j,u=0,l,min;//n为节点总数,s为起始点,m为边数 
    cin>>n>>m;
    for(int i=1,id;i<=n;i++) cin>>id;
    for(int i=1;i<=m;i++)
    {
    	int x,y,w;
    	cin>>x>>y>>w;
    	a[x-1][y-1]=w;
	}
    for(i=0;i<n;i++)//建立邻接矩阵的存储 
    {
        for(j=0;j<n;j++)
        {
            if(i!=j&&a[i][j]==0)
            a[i][j]=N;//如果两个节点没有关系的话就将其值赋值为无穷 
        }
    }
    for(i=0;i<n;i++)
    d[i]=a[s][i]; //初始化起始顶点到每个顶点最短路径的长度,即直接连接两个顶点的有向线段的权重
	//事后再更新其值 
    visit[s]=1;  	//判断节点是否访问过 
    for(i=0;i<n;i++)//大循环,循环n次,每循环一次并入一个未访问的节点,保证每个节点都被并入数组 
    {
        min=N;
        for(j=0;j<n;j++)
        {
            if(min>d[j]&&visit[j]==0)
            {
                min=d[j];
                u=j;	//遍历所有未访问过的节点,找出已访问的节点到未访问的节点距离中的最小值 
            }
        }
        visit[u]=1;//将最小值的节点并入数组 
        for(l=0;l<n;l++)
        {
            if(a[u][l]<N)
            {
                if(d[l]>d[u]+a[u][l])
                d[l]=d[u]+a[u][l];
            }//更新的d[]数组中的值,如果新并入的节点到其他节点的值小于原来的值,就将原来的值更新成最小值 
        }
    }
    for(i=0;i<n;i++)
    {
        if(i==s)
        continue;//起始顶点到起始顶点不算 
        if(d[i]==N)
        cout<<"-1"<<endl;//如果没有最短路径的话,就输出-1 
        else
        cout<<d[i]<<endl;
    }
    cout<<endl;
    return  0;
 }

布线问题(广搜)

【问题描述】

印刷电路板将布线区域划分成n*m个方格阵列,精确的电路布线问题要求确定连接方格a的中点到方格b的中点的最短布线问题。在布线时,电路只能沿着直线或直角布线。为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。

【输入形式】输入共有n+3行
第一行包括两个数,n,m表示电路板是一个n*m的方格阵列
第二行包括两个数,row1,col1,表示起始点的行号和列号
第三行包括两个数,row2,col2,表示目标点的行号和列号
然后输入n行,每行m个数,0或1,表示电路板的封锁情况,1表示电路封锁

【输出形式】输出只有一行
表示从起始点到目的点的最短距离(第1步在起始点,输出到达目标点的步数)

【样例输入】

10 10
1 1

3 4

0 0 0 0 0 0 0 0 0 0

0 0 0 1 0 0 0 0 0 0

0 1 1 1 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0

【样例输出】

8

#include
using namespace std;
const int N=1e3+10;
int n,m,row1,col1,row2,col2;
int a[N][N];
int ans=N; //最终结果 
bool vis[N][N];
int dr[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
struct node
{
	int x,y,steps;
};
queue <node> q;
void bfs()
{
	while(!q.empty())
	{
		node t1=q.front();
		for(int i=0;i<4;i++)
		{
			int xx=t1.x+dr[i][0];
			int yy=t1.y+dr[i][1];
			if(xx==row2 && yy==col2)
			{
				ans=min(ans,t1.steps+1);
				continue;
//				ans=t1.steps+1;
//				return;
			}
			if(xx>=1 && xx<=n && yy>=1 && yy<=m && !vis[xx][yy])
			{
				vis[xx][yy]=1;
				node t2;
				t2.x=xx, t2.y=yy,t2.steps=t1.steps+1;
				q.push(t2);
			} 
		}
		q.pop();
	}
}
int main()
{
	cin>>n>>m>>row1>>col1>>row2>>col2;
	node n1;
	n1.x=row1,n1.y=col1,n1.steps=1;
	vis[row1][col1]=1;
	q.push(n1); // 初始化 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>vis[i][j];
	bfs();
	cout<<ans<<endl;
	return 0;
}

最小机器重量设置

【问题描述】

问题描述:设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设 wij是从供应商j处购得的部件 i 的重量, cij 是相应的价格。试设计一个优先队列式分支限界法算法,试设计一个算法,给出总价格不超过d的最小重量机器设计。

【输入形式】

第一行有3个正整数n , m , d 。接下来的2n 行,每行n个数。前n行是c ,后n 行是w

【输出形式】

最小重量及每个部件的供应商

【样例输入】

8 18 14
18 15 20 5 15 10 16 6 1 6 17 6 1 2 17 15 13 17
16 6 7 4 7 2 11 6 18 4 13 12 8 5 2 8 15 14
12 6 19 10 13 8 2 10 16 4 15 15 16 13 17 12 14 4
18 18 2 13 15 19 5 12 18 7 13 9 8 17 10 13 15 11
8 5 14 11 18 20 17 3 11 17 13 11 4 9 17 14 19 1
10 7 8 11 13 3 19 3 12 11 12 14 4 2 12 10 14 15
12 9 13 9 16 17 12 15 6 3 11 17 13 17 14 13 4 4
19 12 3 19 3 20 19 12 8 19 8 10 19 20 3 1 7 1
16 12 4 16 2 6 15 1 13 3 7 16 5 3 16 16 14 19
12 14 6 2 11 15 9 17 15 16 19 20 14 14 20 9 4 4
6 13 16 6 3 12 12 19 11 20 4 13 9 18 7 17 8 1
4 17 3 20 3 8 12 7 4 12 6 12 1 18 13 20 20 8
4 15 1 10 2 12 8 11 5 4 20 13 12 20 1 3 3 11
1 9 2 1 16 1 12 4 5 2 7 15 12 3 9 4 13 6
13 1 10 8 5 13 20 10 6 4 8 15 8 8 20 11 9 9
2 10 11 1 18 8 20 11 18 2 3 6 14 16 19 4 3 15

【样例输出】

57
13 6 7 3 18 14 10 16

#include
using namespace std;
int n, m, d, min_weigth=0x3f3f3f;
int ans[100];
int c[100][100], w[100][100];
struct node{
    int weigth, cost, level, num, value[100];
    node(int a, int b, int c, int d):weigth(a), cost(b), level(c), num(d){}
    bool operator<(const node& a) const{//小根堆(返回true,则证明前者优先级低于后者,则后者居上!)
        if (weigth!=a.weigth)
            return weigth>a.weigth;//例:倘若weigth>a.weigth为true,则a的优先级更高,即weigth小的对象优先级更高!
        else if(level!=a.level)
            return a.level<level;
        else 
            return num>a.num;
    }
};
priority_queue<node> q;//小根堆
int main()
{
    cin>>n>>m>>d;
    for(int i=0;i<n;i++){//c
        for(int j=0;j<m;j++){
            cin>>c[i][j];
        }
    }
    for(int i=0;i<n;i++){//w
        for(int j=0;j<m;j++){
            cin>>w[i][j];
        }
    }
    for(int i=0;i<m;i++){//优先队列初始化
        node temp=node(w[0][i], c[0][i], 0, i);
        if(temp.cost>d)//剪枝(价格)
            continue;
        else{
            temp.value[0]=i+1;
            q.push(temp);
        }
    }
    while(!q.empty()){
        node temp=q.top();
        q.pop();
        if(temp.level<n-1){
            for(int i=0;i<m;i++){
                node t=node(temp.weigth+w[temp.level+1][i], temp.cost+c[temp.level+1][i], temp.level+1, i);
                if(t.level==n-1){//到达叶子节点
                    if(t.weigth>min_weigth||t.cost>d)//剪枝(重量and价格)
                        continue;
                    else if(t.weigth<min_weigth){
                        min_weigth=t.weigth;//最小重量更新
                        for(int i=0;i<n-1;i++){//最优路径更新
                            ans[i]=temp.value[i];
                        }
                        ans[n-1]=i+1;
                    }
                }
                if(t.level<n-1){//非叶子节点
                    if(t.cost>d){//剪枝(价格)
                        continue;
                    }
                    else{
                        for(int i=0;i<t.level;i++)//历史路径
                            t.value[i]=temp.value[i];
                        t.value[t.level]=i+1;//路径更新
                        q.push(t);
                    }
                }
            }
        }
    }
    cout<<min_weigth<<endl;
    for(int i=0;i<n;i++){
        cout<<ans[i]<<" ";
    }
    return 0;
}

你可能感兴趣的:(c++,算法,算法,c++,c语言)