目录
实验8 求解最小机器重量设计问题I
问题描述:

实现代码:
#include<iostream>
#include<cstring>
#include<vector>
#include<utility>
using namespace std;
//很像分组背包问题,但其实并不是,需要自己分析一波
const int N = 110;
int n, m, cost;//种类,供应商,最大值。
int f[N][N];//选前 i 个种类 且 总价值不超过j 的最小重量
int v[N][N];// v[种类][编号] = 重量
int w[N][N];// w[种类][编号] = 价值
vector<vector<pair<int ,int>>> path(N, vector<pair<int ,int>>(N));
int traversal(int a, int b)
{
if (!a && !b)
{
return 0;
}
int num = f[a][b];
//求出与上一个的差值,方便寻找
int diff = num - traversal(path[a][b].first, path[a][b].second);
for (int i = 1; i <= m; i++)
{
if (diff == v[a][i])
{
cout << i << " ";
break;
}
}
return f[a][b];
}
int main()
{
cin >> n >> m >> cost;
//读入数据
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> v[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
//初始化,很重要,一定要想清楚
//for (int i = 1; i <= n; i++)
//{
// f[i][0] = 1e9;
//}
f[0][0] = 0; //可以不写
//这里的背包是价格,重量是一般背包问题中的价值,不要理解错
//遍历顺序,种类,背包,一种种类中的物品, 应该可以一维优化(滚动数组),但是这里为了方便理解,就用二维了
for (int i = 1; i <= n; i++)
for (int j = 1; j <= cost; j++)
{
f[i][j] = 1e9;
int t = f[i][j]; //记录路径判断用的t
for (int k = 1; k <= m; k++)
{
// 根据差值,找出所选物品编号
if (j >= w[i][k])
{
f[i][j] = min(f[i - 1][j - w[i][k]] + v[i][k], f[i][j]);
if (t != f[i][j]) path[i][j] = { i - 1, j - w[i][k] };
}
}
}
puts("调试-----------------------------");
//打印dp数组调试代码
cout << "dp[i][j]数组:" << endl;
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= cost; j++)
cout << f[i][j] << " ";
cout << endl;
}
//打印路径,path[i][j]表示f[i][j]是由哪个状态转移而来
cout << "路径:path[i][j]表示f[i][j]是由哪个状态转移而来" << endl;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= cost; j++)
{
cout << path[i][j].first << "-" << path[i][j].second << " ";
}
cout << endl;
}
puts("------------------------------");
puts("最终路径:");
int t = traversal(3, cost);//打印最终路径
cout << endl;
cout << "最小重量为:" << f[3][cost] << endl;
}
运行结果:
原理思路:
注释写的很详细了,就不解释了,如果不打印路径其实很简单就能写出来,用dp的话很难打印路径,其实最好还是用其他方法写比较好,这里只是提供一种思路。
实验11 并查集算法 求解奶酪问题
问题描述:

输入输出格式:

样例:

实现代码:
#include<iostream>
using namespace std;
const int N = 1010;
typedef long long LL;
LL p[N];
LL x[N], y[N], z[N]; // 坐标
// 并查集
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 检查是否连通
bool check(LL a, LL b, LL r)
{
LL dist = (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])+(z[a]-z[b])*(z[a]-z[b]);
if (dist > 4 * r * r) return false;
return true;
}
int main()
{
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int t;
cin >> t;
int c = t;
while (t--)
{
LL n, h, r;
cin >> n >> h >> r;
for (int i = 1; i <= n; i++) cin >> x[i] >> y[i] >> z[i];
for (int i = 0; i <= n + 1; i++) p[i] = i; // 初始化
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
{
if (check(i, j, r)) p[find(i)] = find(j);
}
// 把上下表面外的空间也看作是两个空洞, 0位置为下,n + 1 的位置为上
for (int i = 1; i <= n; i++) if (z[i] <= r) p[find(i)] = find(0);
for (int i = 1; i <= n; i++) if (z[i] >= h - r) p[find(i)] = find(n + 1);
// 查看上下表面当作的两个空洞是否连通
if (find(0) == find(n + 1)) puts("Yes");
else puts("No");
}
return 0;
}
原理思路:
并查集写法,把上下表面外的空间也看作为一个空洞,判断两个空洞是否相连,若相连则合并到一起,记得把和上下底面相连的也合并到一起,最好判断上下面是否相通即可,我这里用索引0位置表示下底面外的空间,1位置表示上面外的空间。(记得开long long)
实验12 最小生成树算法 求解全省畅通工程的最低成本问题
问题描述:
输入输出:
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int g[N][N];
int dist[N];
int n;
int prim()
{
memset(dist, 0x3f, sizeof dist);
bool st[N] = {false};
int res = 0;
dist[1] = 0;
for (int i = 0; i < n; i++)
{
int t = -1;
for (int j = 1; j <= n; j++)
{
if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
}
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j++)
{
dist[j] = min(dist[j], g[t][j]);
}
}
return res;
}
int main()
{
while (cin >> n)
{
memset(g, 0x3f, sizeof g);
if (n == 0) break;
for (int i = 1; i <= n; i++)
{
int a, b, w, c;
cin >> a >> b >> w >> c;
if (c == 1) w = 0;
g[a][b] = g[b][a] = w;
}
int t = prim();
if (t == INF) puts("没有");
else cout << t << endl;
}
}
原理思路:
prim算法而已,已经修好路的,就把边的权重赋值为0即可,还有注意这里st数组放进prim函数里,平时prim就用一次初始化一次就行,习惯了写在外面,现在有多个样例,会用到多次prim算法,所以每次都要初始化一下,这个错误找了半天,我就说为什么第一个样例结果就是对的,我还以为是代码某个地方写错了,调试了半天才发现这个问题,也算是长了个记性。