数据结构实验

7-1 整数拆分

作者 谷方明
单位 吉林大学
整数拆分是一个古老又有趣的问题。请给出将正整数 n 拆分成 k 个正整数的所有不重复方案。例如,将 5 拆分成 2 个正整数的不重复方案,有如下2组:(1,4)和(2,3)。注意(1,4) 和(4,1)被视为同一方案。每种方案按递增序输出,所有方案按方案递增序输出。

输入格式:
1行,2个整数n和k,用空格分隔, 1≤k≤n≤50.

输出格式:
若干行,每行一个拆分方案,方案中的数用空格分隔。
最后一行,给出不同拆分方案的总数。

输入样例:
在这里给出一组输入。例如:
5 2

输出样例:
在这里给出相应的输出。例如:
1 4
2 3
2

#include <iostream>
#include <vector>
using namespace std;
int n, k;
int count;
vector<int> temp;
void dfs(int sum, int start)
{
    if (sum > n || (sum < n && temp.size() >= k))
        return;
    else if (sum == n && temp.size() == k)
    {
        count++;
        for (int i = 0; i < temp.size() - 1; i++)
        {
            cout << temp[i] << " ";
        }
        cout << temp[temp.size() - 1] << endl;
        return;
    }
    for (int i = start; i <= n; i++)
    {
        temp.push_back(i);
        dfs(sum + i, i);
        temp.pop_back();
    }
}
int main()
{
    cin >> n >> k;
    dfs(0, 1);

    cout << count;
    return 0;
}

7-2 发红包

作者 谷方明
单位 吉林大学
新年到了,公司要给员工发红包。员工们会比较获得的红包,有些员工会有钱数的要求,例如,c1的红包钱数要比c2的多。每个员工的红包钱数至少要发888元,这是一个幸运数字。
公司想满足所有员工的要求,同时也要花钱最少,请你帮助计算。

输入格式:
第1行,两个整数n和m(n<=10000,m<=20000),用空格分隔,分别代表员工数和要求数。
接下来m行,每行两个整数c1和c2,用空格分隔,表示员工c1的红包钱数要比c2多,员工的编号1~n 。

输出格式:
一个整数,表示公司发的最少钱数。如果公司不能满足所有员工的需求,输出-1.

输入样例:
在这里给出一组输入。例如:
2 1
1 2

输出样例:
在这里给出相应的输出。例如:
1777

代码长度限制16 KB
时间限制50 ms
内存限制10 MB
实现
想了好久也没想出来哪里错了唉

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;
const int N = 10005;

int h[N], e[N], ne[N], idx; // 邻接表
int count[N];               // 存储每个点的入度
long long money[N];         // 存储每个员工的红包钱数
void add(int a, int b)      // 添加一条边
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void TopoOrder(int n) // n为员工数
{
    queue<int> q; // 用队列存储入度为0的点

    // 将入度为0的点入队列
    for (int i = 1; i <= n; i++)
        if (count[i] == 0)
        {
            money[i] = 888;
            q.push(i);
        }

    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int nextNode = e[i];
            count[nextNode]--;
            money[nextNode] = max(money[nextNode], money[t] + 1);
            if (count[nextNode] == 0)
                q.push(nextNode);
        }
    }
}

int main()
{
    idx = 0;
    memset(h, -1, sizeof(h));
    memset(count, 0, sizeof(count));
    memset(money, 0, sizeof(money));

    int n, m;
    cin >> n >> m;
    if (n == 0)
    {
        cout << 0;
        exit(0);
    }
    if (m == 0)
    {
        cout << n * 888;
        exit(0);
    }
    while (m--)
    {
        int c1, c2;
        scanf("%d%d", &c1, &c2);
        add(c1, c2);
        count[c2]++;
    }
    TopoOrder(n);
    for (int i = 1; i <= n; i++)
    {
        if (count[i] != 0)
        {
            cout << -1;
            exit(0);
        }
    }

    long long res = 0;
    for (int i = 1; i <= n; i++)
    {
        res += money[i];
    }
    printf("%lld", res);

    return 0;
}

标准答案
拓扑排序

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

const int INF = 1e9 + 5;
const int N = 10005;
typedef long long ll;

vector<int> h[N]; // 邻接表存储
queue<int> q;     // 存储入度为0的点
int n, m;
int count[N]; // 存储入度
int money[N]; // 存储每个点的金额
ll res;

void f() // 拓扑排序
{
    for (int i = 1; i <= n; i++)
    {
        if (count[i] == 0)
            q.push(i), money[i] = 888;
    }

    while (!q.empty())
    {
        int t = q.front();
        q.pop();

        for (int j = 0; j < h[t].size(); j++)
        {
            money[h[t][j]] = max(money[h[t][j]], money[t] + 1);
            count[h[t][j]]--;
            if (count[h[t][j]] == 0)
                q.push(h[t][j]);
        }
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    if (n == 0)
    {
        cout << 0;
        exit(0);
    }
    if (m == 0)
    {
        cout << n * 888;
        exit(0);
    }

    int x, y;
    while (m--)
    {
        scanf("%d%d", &x, &y);
        h[y].push_back(x);
        count[x]++;
    }
    f();
    for (int i = 1; i <= n; i++)
    {
        if (count[i] != 0)
        {
            cout << -1;
            exit(0);
        }
    }
    for (int i = 1; i <= n; i++)
        res += money[i];

    cout << res;
    return 0;
}

7-3 供电

作者 谷方明
单位 吉林大学
要给N个地区供电。每个地区或者建一个供电站,或者修一条线道连接到其它有电的地区。试确定给N个地区都供上电的最小费用。

输入格式:
第1行,两个个整数 N 和 M , 用空格分隔,分别表示地区数和修线路的方案数,1≤N≤10000,0≤M≤50000。
第2行,包含N个用空格分隔的整数P[i],表示在第i个地区建一个供电站的代价,1 ≤P[i]≤ 100,000,1≤i≤N 。
接下来M行,每行3个整数a、b和c,用空格分隔,表示在地区a和b之间修一条线路的代价为c,1 ≤ c ≤ 100,000,1≤a,b≤N 。

输出格式:
一行,包含一个整数, 表示所求最小代价。

输入样例:
在这里给出一组输入。例如:
4 6
5 4 4 3
1 2 2
1 3 2
1 4 2
2 3 3
2 4 3
3 4 4

输出样例:
在这里给出相应的输出。例如:
9

核心:将站点的代价抽象为虚拟点,点的代价抽象为点与点之间的边权值,通过最小生成树的克鲁斯卡尔算法实现
代码实现

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 10005, M = 1e5 + 10;
int n, m; // n是点数,m是边数
int p[N]; // 并查集的父节点数组

struct Edge // 存储边
{
	int a, b, w;
	bool operator<(const Edge &W) const
	{
		return w < W.w;
	}
} edges[M];

int find(int x) // 并查集核心操作
{
	if (p[x] != x)
		p[x] = find(p[x]);
	return p[x];
}

int kruskal()
{
	sort(edges, edges + m + n); // 将所有边权重排序

	for (int i = 1; i <= n + 1; i++)
		p[i] = i; // 初始化并查集

	int res = 0, cnt = 0; // res存储最小生成树的权值和,cnt存储目前最小生成树点的个数

	for (int i = 0; i < m + n; i++) // 遍历每一条边
	{
		int a = edges[i].a, b = edges[i].b, w = edges[i].w;

		a = find(a), b = find(b);
		if (a != b) // 如果两个连通块不连通,则将这两个连通块合并
		{
			p[a] = b;
			res += w;
			cnt++;
		}
		if (cnt == n )
			break;
	}
	return res;
}

int main()
{
	cin >> n >> m;
	int idx = 0;
	for (int i = 1; i <= n; i++)
	{
		int x;
		cin >> x;
		edges[idx].a = i;
		edges[idx].b = n + 1;
		edges[idx++].w = x;
	}

	for (int i = 1; i <= m; i++)
	{
		int a, b, w;
		scanf("%d%d%d", &a, &b, &w);
		edges[idx].a = a;
		edges[idx].b = b;
		edges[idx++].w = w;
	}
	cout << kruskal() << endl;
	return 0;
}

7-4 交换次数

作者 谷方明
单位 吉林大学
序列A中有N个整数。
求对A进行冒泡排序发生的元素交换次数。

输入格式:
第一行输入整数N(2<=N<=10^6).
接下来一行N个正整数数A[i] (1≤i≤N ,A[i]<=10^6)。

输出格式:
一行,有一个整数,表示元素交换的次数。

输入样例:
在这里给出一组输入。例如:
4
2 4 3 1
输出样例:
在这里给出相应的输出。例如:
4

代码长度限制
16 KB
时间限制
400 ms
内存限制
30 MB

错误做法

#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n;
int arr[N];
int cnt;
void bubble_sort()
{
    bool flag = true;
    while (flag)
    {
        flag = false;
        for (int i = 0; i < n - 1; i++)
        {
            if (arr[i] > arr[i + 1])
            {
                cnt++;
                flag = true;
                int t = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = t;
            }
        }
    }
}
int main()
{

    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> arr[i];
    }
    
    bubble_sort();
    cout << cnt;
    return 0;
}

正确写法

#include <iostream>
using namespace std;

long long merge(int arr[], int l, int mid, int r, int tmp[])
{
    long long count = 0;

    int i = l, j = mid + 1, k = l;
    while (i <= mid && j <= r)
    {
        if (arr[i] <= arr[j])
        {
            tmp[k++] = arr[i++];
        }
        else
        {
            tmp[k++] = arr[j++];
            count += mid - i + 1; // 统计逆序对的数量
        }
    }

    while (i <= mid)
        tmp[k++] = arr[i++];
    while (j <= r)
        tmp[k++] = arr[j++];

    for (int i = l; i <= r; i++)
        arr[i] = tmp[i];

    return count;
}

long long mergeSort(int arr[], int l, int r, int tmp[])
{
    long long count = 0;

    if (l < r)
    {
        int mid = l + (r - l) / 2;
        count += mergeSort(arr, l, mid, tmp);     // 统计左半部分的逆序对数量
        count += mergeSort(arr, mid + 1, r, tmp); // 统计右半部分的逆序对数量
        count += merge(arr, l, mid, r, tmp);      // 合并两部分并统计跨越两部分的逆序对数量
    }

    return count;
}

long long countInversions(int arr[], int n)
{
    int tmp[n];
    return mergeSort(arr, 0, n - 1, tmp);
}

int main()
{
    int n;

    cin >> n;

    int arr[n];

    for (int i = 0; i < n; i++)
        cin >> arr[i];

    long long inversions = countInversions(arr, n);
    cout << inversions << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值