致大四学长的作业

1T. 项目投资效益最大化问题

思路

  1. d p ( i , j ) dp(i,j) dp(i,j)表示从前 i i i个物品中选,目前已用总资金为 j j j万元时,可以达到的最大效益。并设 v i , j v_{i,j} vi,j表示第 i i i个项目投入 j j j万元时,可以获得的效益。则有状态转移方程 d p i , j = max ⁡ 0 ≤ k ≤ j { d p i − 1 , j − k + v i , k } dp_{i,j}=\max\limits_{0\le k\le j}\{dp_{i-1,j-k}+v_{i,k}\} dpi,j=0kjmax{dpi1,jk+vi,k}
  2. 此时,时间复杂度和空间复杂度为 O ( N V ) O(NV) O(NV),其中 N N N为项目的数量, V V V为总资金。
  3. 可以用滚动数组优化空间复杂度为 O ( V ) O(V) O(V)

代码(无滚动数组优化版)

# 每个数据之间用空格隔开
# 输入总项目数和总资金
N,V=[int(e) for e in input().split()]
v=[[0 for j in range(V+1)] for i in range(N+1)]
dp=[[0 for j in range(V+1)] for i in range(N+1)]
# 第 i 行依次输入投入资金为 j 时的收益
for i in range(1,N+1):
	v[i]=[0]+[int(e) for e in input().split()]
# 动态规划(无滚动数组)
for i in range(1,N+1):
	for j in range(1,V+1):
		for k in range(j+1):
			dp[i][j]=max(dp[i][j],dp[i-1][j-k]+v[i][k])
# 输出答案
print(dp[N][V])
# 测试用例
'''
input
3 3
15 28 40
13 29 43
11 30 35
output
45
'''

代码(滚动数组优化版)

# 每个数据之间用空格隔开
# 输入总项目数和总资金
N,V=[int(e) for e in input().split()]
v=[[0 for j in range(V+1)] for i in range(N+1)]
dp=[0 for j in range(V+1)]
# 第 i 行依次输入投入资金为 j 时的收益
for i in range(1,N+1):
	v[i]=[0]+[int(e) for e in input().split()]
# 动态规划(滚动数组)
for i in range(1,N+1):
	# 这里 j 一定要倒序遍历
	for j in range(V,0,-1):
		for k in range(j+1):
			dp[j]=max(dp[j],dp[j-k]+v[i][k])
# 输出答案
print(dp[V])
# 测试用例
'''
input
3 3
15 28 40
13 29 43
11 30 35
output
45
'''

2T. 交通网最短路问题

思路

  1. 易发现,所有边权均为正边权,故采用堆优化的dijkstra算法。
  2. 对于无向边,我们可以进行在两点之间建两条有向边。例如,假设 u u u v v v之间有一条无向边,则从 u u u v v v建一条有向边,再从 v v v u u u建一条有向边。
  3. 对于第(2)问的寻找路径,可以用 f a i fa_i fai去维护从 V 1 V_1 V1出发到点 V i V_i Vi的最短路径中直接与 i i i相邻的节点(即 i i i的前继节点)。然后从终点一直向其前继节点回溯,直到回溯到终点为止,最后倒序输出刚才参与回溯的点即可。注:此时仅为寻找一条最短路径,当最短路径有多条时,可以用vector来存 V i V_i Vi所有的前继节点,用深度优先搜索输出所有的解即可。
  4. 时间复杂度为 O ( ( n + m ) log ⁡ ( n + m ) ) O((n+m)\log (n+m)) O((n+m)log(n+m)),其中 n n n为节点数, m m m为边数。

代码

#include <bits/stdc++.h>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn=1e5+100;
const int maxm=5e5+100;
int n,m,s;
// 链式前向星存图
int head[maxn],cnt;
struct edge 
{
	int v,next;
	double w;
}e[maxm<<1];
void add(int u,int v,double w) 
{
	e[++cnt]=(edge){v,head[u],w};
	head[u]=cnt;
}
struct node 
{
	int pos;
	double dist;
	bool operator < (node a) const 
	{
		return a.dist<dist;
	}
};
priority_queue<node> q,emptyi;
int fa[maxn];bool vis[maxn];
double dis[maxn];
void dijkstra() 
{
	// 初始化 fa_i 为 i
	for(int i=1;i<=n;i++) fa[i]=i;
	// dijkstra 算法
	for (int i=1;i<=n;i++)
		dis[i]=2e9;
	dis[s]=0; q.push((node){s,0});
	while(!q.empty ()) 
	{
		int u=q.top().pos; q.pop();
		if(vis[u]) continue;
		vis[u]=true;
		for(int i=head[u];i;i=e[i].next)  
		{
			int v=e[i].v; double w=e[i].w;
			if (dis[v]>dis[u]+w) 
			{
				dis[v]=dis[u]+w;
				fa[v]=u;
				if(!vis[v]) q.push((node){v,dis[v]});
			}
		}
	}
	// 从左到右依次输出,起点到编号为 i 的点的最短距离。
	for(int i=1;i<=n;i++)
		printf("%lf ",dis[i]);
		printf("\n");
	// 输入所要寻找路径的终点
	int st[maxn],p,cur_point;
	scanf("%d",&cur_point);
	// 若起点到其的最短距离为INF(即无穷大,本题设为 2e9),
	// 则说明无起点与之不可通达,故输出 No solution.
	if(dis[cur_point]==2e9) printf("No solution");
	else
	{
		while(cur_point!=fa[cur_point])
			st[++p]=cur_point,cur_point=fa[cur_point];
		st[++p]=cur_point;
		// 倒序输出参与回溯的节点。
		for(int i=p;i>=1;i--)
			printf("%d ",st[i]);
	}
}
int main ()
{
	// 输入总点数,总边数和起点。
	scanf ("%d%d%d",&n,&m,&s);
	for(int i=1;i<=m;i++) 
	{
		int u,v,tp; double w;
		//输入两点的编号,边权,以及边的类型。
		// 当 tp 为 0 时,为u->v的有向边;tp 为 1 时,为无向边。
		scanf("%d%d%lf%d",&u,&v,&w,&tp);
		if(tp==0) add(u,v,w);
		if(tp==1) add(u,v,w),add(v,u,w);
	}
	dijkstra ();
	return 0;
}
// 测试样例,即试卷问题
/*
input
9 13 1
1 2 3 0
1 4 4 0
2 3 3 0
2 6 3 0
2 5 2 0
5 6 3 0
4 7 3 0
6 7 1 1
3 9 5 0
6 9 2.5 0
7 9 2 0
7 8 2 0
8 9 4 0
9
output
0.000000 3.000000 6.000000 4.000000 5.000000 6.000000 7.000000 9.000000 8.500000
1 2 6 9
*/

3T. 对伪代码进行分析

# 设 P,W,C 分别为二维数组 w,c,p 存储的矩阵。
# 且满足 W  (i,j) 元与 w[i,j] 相等;CP 同理。
# 该函数主要实现用二维数组 p 存储矩阵 P,并返回 p。其中 P=WCMatMult(p[n,n],w[n,n],c[n,n])
{
# 矩阵乘法的对应法则。
for i <- 0 to n-1 do
	for j <- 0 to n-1 do
	{
		p[i,j]=0.0;
		for k <- 0 to n-1 do
			p[i,j]=p[i,j]+w[i,k]*c[k,j]
	
	}
	return p;
}

4T. 蒙特卡罗法估计积分的近似值

思路

  1. 蒙特卡罗法估计积分的近似值,主要是借助了几何概率。一个关于几何概率的简单例子:现有一面积为 S S S的矩形 A,该矩形完全包含一个面积为 S ′ ( S ≥ S ′ ) S'(S\ge S') S(SS)的任意图形 B,则随机向A中投射一个点,该点落在 B 中的概率 p = S ′ S p=\frac{S'}{S} p=SS。当 S S S p p p已知时,可知 S ′ = S ∗ P S'=S*P S=SP
  2. 对于该题的积分近似值,可以通过放缩得到一个完全包含 y = f ( x ) , x ∈ [ 1 , 3 ] y=f(x),x\in[1,3] y=f(x),x[1,3]的图像,即 2 ≤ 6 x 2 − 4 ≤ f ( x ) = 6 x 2 − sin ⁡ ( x ) − 3 ≤ 6 x 2 − 2 ≤ 52 , x ∈ [ 1 , 3 ] 2\le6x^2-4\le f(x)=6x^2-\sin(x)-3\le6x^2-2\le52,x\in[1,3] 26x24f(x)=6x2sin(x)36x2252,x[1,3]。但由于 f ( x ) f(x) f(x)的最小值不可能小于 2 2 2,而黎曼积分可以看作曲线与 x x x轴的正负面积之和,故我们需要把矩形的下边扩大到! 0 0 0。故建立一个矩形,其四条边分别为 y = 0 , x ∈ [ 1 , 3 ] y=0,x\in[1,3] y=0,x[1,3] y = 52 , x ∈ [ 1 , 3 ] y=52,x\in[1,3] y=52,x[1,3] x = 1 , y ∈ [ 0 , 52 ] x=1,y\in[0,52] x=1,y[0,52] x = 3 , y ∈ [ 0 , 52 ] x=3,y\in[0,52] x=3,y[0,52],进而可知 S = 104 S=104 S=104。同时,由于 f ( x ) ≥ 0 , x ∈ [ 1 , 3 ] f(x)\ge0,x\in[1,3] f(x)0,x[1,3],故不存在负面积,即只需要统计随机投射的 N N N个点中位于 x x x轴与曲线之间点的数量 M M M,则 p = M N p=\frac{M}{N} p=NM,进而可求 S ′ S' S

代码

# 投射 1e6 个点,一般民用计算机可以负担,且可快速运行。
N,M,S=10**6,0,104
f=lambda x:6*x**2-sin(x)-3
from math import sin
import numpy.random as nprdm
# 随机生成N个点的坐标(x,y)
x=nprdm.uniform(1,3,size=N)
y=nprdm.uniform(0,52,size=N)
for i in range(N):
	if y[i]<=f(x[i]):
		M+=1
print(S*M/N)
'''
maybeoutput
44.535504
'''

5T. Gauss-Seidel 算法求解线性方程组

这个题是个不加修改的板子,学长自己去看看吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值