SDUT-合法拓扑序列(Not unique)

本文介绍有向无环图(DAG)的概念及其应用,并详细解释拓扑排序的实现方法。通过实例展示了如何利用拓扑排序解决实际工程问题。

http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/2140.html

有向无环图

一个无环的有向图称作有向无环图(directed acycline graph),简称DAG图。假设以有向图表示一个工程的施工图或程序的数据流图,则图中不允许出现回路,如果出现回路,说明了某项活动以它自己为先决条件,显然是荒谬的,工程将无法进行。 [1] 

拓扑排序

拓扑排序是一种对非线性结构的有向图进行线性化的重要手段。在给定的有向图G中,若顶点序列Vi1,Vi2,Vi3,....,Vin,。满足下列条件:若在有向图G中从顶点Vi,到顶点Vj有一条路径,则在序列中顶点Vi必在顶点Vj之前,便称这个序列为一个拓扑序列。求一个有向图拓扑序列的过程称为拓扑排序。
  拓扑排序的方法如下:
  (1)从图中选择一个人度为O的顶点并输出;
  (2)从图中删掉该顶点及其所有以该顶点为弧尾的弧。
  反复执行这两个步骤,直到所有的顶点都被输出,输出的序列就是这个无环有向图的拓扑序列。

如果在带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的开销,则此带权有向图称为边活动网(activity on edge network),简称AOE网。AOE网是一个有向无环图。AOE网是用来描述由许多交叉活动组成的复杂计划和工程的方法,比如某工程的AOE网。

在工程中用边表示活动,边上的权表示完成这项活动所需要的时间,顶点表示某项活动的开始,顶点1称为源点(或起点),表示整个工程开始,顶点2称为汇点(或终点),表示整个工程的结束。用AOE网来估算工程的最短工期(完成整个工程至少需要多少时间)以及哪些活动是影响工程进展的关键。

注意逻辑关系:

if(mmp[temp][j]==1)

{

ingraph[j]--; i

f(ingraph[j]==0)

que.push(j);

}

#include<bits/stdc++.h>
using namespace std;
int ingraph[15];
int mmp[15][15];
int n,m,sum;
void judge()
{
    queue<int>que;
    for(int i=1; i<=n; i++)
        if(ingraph[i]==0)
            que.push(i);
    while(!que.empty())
    {

        int temp=que.front();
        que.pop();
        sum++;
        for(int j=1; j<=n; j++)
        {
            if(mmp[temp][j]==1)
            {
                ingraph[j]--;
                if(ingraph[j]==0)
                    que.push(j);
            }
        }
    }
    printf(n==sum?"YES\n":"NO\n");
}
int main()
{
    int u,v;
    while(cin>>n>>m)
    {
        sum=0;
        memset(ingraph,0,sizeof(ingraph));
        memset(mmp,0,sizeof(mmp));
        for(int i=0; i<m; i++)
        {
            cin>>u>>v;
            mmp[u][v]=1;
            ingraph[v]++;
        }
        judge();
    }
    return 0;
}
<think> 根据用户的问题,用户想要查找关于“7-20 sdut C语言实验 分数序列”问题的解决方案或资料。从提供的引用中,我们并没有直接看到关于分数序列的内容。但是,我们可以根据题目编号“7-20”推测这可能是某个在线评测系统(如SDUT)上的一道题目,通常涉及编程解决问题。 由于引用中没有直接相关的代码或分析,我们需要根据题目名称“分数序列”进行推测。常见的分数序列问题可能是计算一个特定分数序列的前n项和,例如: 有一个分数序列:2/1, 3/2, 5/3, 8/5, 13/8, ...(即从第二项起,每一项的分子是前一项分子与分母之和,分母是前一项的分子),求这个数列的前n项和。 因此,我们可以假设题目要求是:输入一个整数n,计算并输出该分数序列的前n项和(保留两位小数)。 ### 解决方案思路 1. **理解序列规律**: - 设分子为a,分母为b。 - 第一项:a1=2, b1=1 - 第二项:a2=3, b2=2 - 第三项:a3=5, b3=3 - 第四项:a4=8, b4=5 - 规律:a_i = a_{i-1} + b_{i-1}, b_i = a_{i-1} (i>=2) 或者也可以这样观察: - 分子:2, 3, 5, 8, 13, ... 满足斐波那契数列的扩展(从2开始) - 分母:1, 2, 3, 5, 8, ... 也是斐波那契数列的扩展(从1开始,但第二项是2) 实际上,这个序列的分子和分母都是斐波那契数列的一部分。我们可以用两个变量分别表示当前的分子和分母,然后迭代更新。 2. **迭代计算**: - 初始化:sum=0, a=2, b=1 - 循环n次: sum += (double)a / b; 更新:temp = a; // 保存当前的分子 a = a + b; // 下一个分子是当前分子+分母 b = temp; // 下一个分母是当前的分子 3. **注意**:由于整数除法会截断小数部分,需要将分子或分母转换为double类型再进行除法运算。 ### 代码实现 下面是一个可能的C语言实现: ```c #include <stdio.h> int main() { int n, i; double sum = 0.0; int a = 2, b = 1; // 第一项分子a=2,分母b=1 scanf("%d", &n); // 输入n for (i = 0; i < n; i++) { sum += (double)a / b; // 将当前项加到和上 int temp = a; // 临时保存当前分子 a = a + b; // 更新下一个分子 b = temp; // 更新下一个分母 } printf("%.2f\n", sum); // 输出结果,保留两位小数 return 0; } ``` ### 代码说明 - 我们使用整型变量`a`和`b`分别表示分子和分母,初始化为2和1。 - 循环`n`次,每次将当前分数`a/b`加到`sum`上(注意将其中一个整数转换为double以避免整数除法)。 - 更新规则:用临时变量`temp`保存当前的分子`a`,然后将分子更新为`a+b`,分母更新为`temp`(即原来的分子)。 - 最后输出和,保留两位小数。 ### 测试样例 例如,输入`n=3`,计算过程: - 第一项:2/1 = 2.0 - 第二项:3/2 = 1.5 - 第三项:5/3 ≈ 1.6667 - 和 = 2.0 + 1.5 + 1.6667 ≈ 5.1667,输出5.17(保留两位小数) ### 注意事项 - 注意整数溢出问题:如果n较大,分子和分母可能会超过int的范围。但题目一般会给定n的范围,如果n不大(比如n<40),int足够。如果n较大,可以使用long long类型。 - 浮点数精度:题目要求保留两位小数,使用double类型通常足够。 如果题目要求不同(比如序列定义不同),请根据实际题目调整代码。但根据“分数序列”的常见问题,以上代码应该是适用的。 ### 相关问题 1. 如何优化分数序列的计算以避免大量迭代? 2. 分数序列的项与斐波那契数列有什么关系? 3. 如果要求输出分数序列的前n项(而不仅仅是和),应该如何修改代码? </think> ### 7-20 SDUT C语言实验:分数序列问题解决方案 根据题目描述和常见实验要求,分数序列问题通常要求计算特定规律的分数序列前 $n$ 项和。典型题目要求: > 有一个分数序列:$ \frac{2}{1}, \frac{3}{2}, \frac{5}{3}, \frac{8}{5}, \frac{13}{8}, \frac{21}{13} \cdots $ > 输入正整数 $n$,输出该序列前 $n$ 项和(保留两位小数) #### 算法思路 1. **序列规律分析**: - 分子:$2, 3, 5, 8, 13 \cdots$(满足 $a_i = a_{i-1} + b_{i-1}$) - 分母:$1, 2, 3, 5, 8 \cdots$(满足 $b_i = a_{i-1}$) - 等价于斐波那契数列:分子是 $Fib(n+2)$,分母是 $Fib(n+1)$ 2. **迭代计算**: ```mermaid graph TD Start[输入n] --> Init[初始化:<br>sum=0, a=2, b=1] Init --> Loop[循环i=1 to n] Loop --> Add[sum += a/b] Add --> Update[更新:<br>temp=a, a=a+b, b=temp] Update --> Loop Loop --> Output[输出sum保留两位小数] ``` #### 完整代码实现 ```c #include <stdio.h> int main() { int n; scanf("%d", &n); // 输入项数 double sum = 0.0; // 总和 double a = 2.0, b = 1.0; // 初始分子分母 for (int i = 1; i <= n; i++) { sum += a / b; // 累加当前项 // 更新分子分母:保留当前分子到temp double temp = a; a = a + b; // 新分子 = 原分子+分母 b = temp; // 新分母 = 原分子 } printf("%.2f\n", sum); // 保留两位小数输出 return 0; } ``` #### 关键点说明 1. **数据类型**: - 必须使用 `double` 存储分子分母,避免整数除法截断 - 示例:$ \frac{5}{3} \approx 1.6667 $,若用 `int` 会丢失精度 2. **更新逻辑**: ```c temp = a; // 暂存当前分子 a = a + b; // 下一项分子 = 当前分子+分母 b = temp; // 下一项分母 = 当前分子 ``` 3. **边界处理**: - $n=1$ 时直接输出 $2.00$ - $n=0$ 时输出 $0.00$(题目通常要求 $n \geq 1$) #### 测试用例 | 输入 | 输出 | 计算过程 | |------|------|----------| | `3` | `5.17` | $ \frac{2}{1} + \frac{3}{2} + \frac{5}{3} = 2 + 1.5 + 1.6667 \approx 5.17 $ | | `5` | `10.07` | 前5项和 ≈ $2+1.5+1.6667+1.6+1.625≈10.07$ | ### 常见问题解决 1. **输出格式错误**: - 使用 `printf("%.2f\n", sum)` 确保保留两位小数 - 错误示例:`printf("%d\n", (int)sum)` 会丢失小数 2. **迭代顺序错误**: - 必须先累加 `sum` 再更新分子分母 - 反例:若先更新再累加,会跳过第一项 3. **整数溢出**: - 当 $n > 40$ 时分子可能超过 $10^6$ - 建议始终使用 `double` 避免溢出
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值