一、36进制
题目描述
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
对于 16 进制,我们使用字母 A−F 来表示 10 及以上的数字。
如法炮制,一直用到字母 Z,就可以表示 36 进制。
36 进制中,A 表示 10,Z 表示 35,AA 表示370。
你能算出 MANY 表示的数字用 10 进制表示是多少吗?
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
思路:直接计算36进制的10进制值,没什么好说的。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str_num = "MANY";
int num = 0;
for(int i = 0; i < str_num.size(); ++ i)
{
if(str_num[i] >= '0' && str_num[i] <= '9')
{
num = num * 36 + str_num[i] - '0';
}
else
{
num = num * 36 + str_num[i] - 'A' + 10;
}
}
cout << num;
return 0;
}
二、瓷砖样式
题目描述
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
小明家的一面装饰墙原来是 3×10 的小方格。 现在手头有一批刚好能盖住 2 个小方格的长方形瓷砖。 瓷砖只有两种颜色:黄色和橙色。
小明想知道,对于这么简陋的原料,可以贴出多少种不同的花样来。 小明有个小小的强迫症:忍受不了任何 2×2 的小格子是同一种颜色。 (瓷砖不能切割,不能重叠,也不能只铺一部分。另外,只考虑组合图案,请忽略瓷砖的拼缝) 显然,对于 2×3 个小格子来说,口算都可以知道:一共 10 种贴法,如下图所示:
但对于 3×10 的格子呢?肯定是个不小的数目,请你利用计算机的威力算出该数字。
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
思路:首先明确一点,3*10的格子上要用面积为2的矩形块填满,因此总共需要15块,我们可以分别枚举15块矩形块的放置方式(横向、竖向),以及矩形块的颜色。
#include <iostream>
#include <cstring>
#include <unordered_set>
using namespace std;
unordered_set<string> blockState;
int blocks[4][15];
bool checkSquare(int x, int y)
{
int value = blocks[x][y];
return value == blocks[x + 1][y] && value == blocks[x][y + 1] && value == blocks[x + 1][y + 1];
}
bool checkBlocks()
{
for(int i = 1; i <= 2; ++ i)
{
for(int j = 1; j <= 9; ++ j)
{
//if((blocks[i][j] + blocks[i + 1][j] + blocks[i][j + 1] + blocks[i + 1][j + 1]) % 4 == 0)
//if((blocks[i][j] == blocks[i + 1][j] && blocks[i][j + 1] == blocks[i + 1][j + 1]) && blocks[i][j] == blocks[i + 1][j + 1])
if(checkSquare(i,j))
{
return false;
}
}
}
return true;
}
void DFS(int x, int y)
{
if(x > 3)
{
//cout << x << " " << y << endl;
if(checkBlocks())
{
string str = "";
for(int i = 1; i <= 3; ++ i)
{
for(int j = 1; j <= 10; ++ j)
{
str.append(1, '0' + blocks[i][j]);
}
}
blockState.insert(str);
}
return;
}
int nx = y == 10 ? x + 1 : x;
int ny = y == 10 ? 1 : y + 1;
if(blocks[x][y] == -1)
{
if(y < 10 && blocks[x][y] == -1 && blocks[x][y + 1] == -1)//可以向右放
{
blocks[x][y] = blocks[x][y + 1] = 0;
DFS(nx,ny);
blocks[x][y] = blocks[x][y + 1] = 1;
DFS(nx,ny);
blocks[x][y] = blocks[x][y + 1] = -1;
}
if(x < 3 && blocks[x][y] == -1 && blocks[x + 1][y] == -1)//可以向右放
{
blocks[x][y] = blocks[x + 1][y] = 0;
DFS(nx,ny);
blocks[x][y] = blocks[x + 1][y] = 1;
DFS(nx,ny);
blocks[x][y] = blocks[x + 1][y] = -1;
}
}
else{
DFS(nx,ny);
}
}
int main()
{
memset(blocks, -1, sizeof(blocks));
DFS(1, 1);
cout << blockState.size();
return 0;
}
[注]:1.题目中有要求"只考虑组合图案,请忽略瓷砖的拼缝",也就是说颜色排列相同的视为同一种方案,不考虑瓷砖之间的缝隙也就是不考虑到底每块瓷砖如何摆放。
2.在考虑每个2*2的格子不能有相同颜色时,可以用颜色对应的值累加后对4取模判断。
三、希尔伯特曲线
题目描述
本题为代码补全填空题,请将题目中给出的源代码补全,并复制到右侧代码框中,选择对应的编译语言(C/Java)后进行提交。若题目中给出的源代码语言不唯一,则只需选择其一进行补全提交即可。复制后需将源代码中填空部分的下划线删掉,填上你的答案。提交后若未能通过,除考虑填空部分出错外,还需注意是否因在复制后有改动非填空部分产生错误。
希尔伯特曲线是以下一系列分形曲线 H_nHn 的极限。我们可以把 H_n 看作一条覆盖 2^n×2^n 方格矩阵的曲线,曲线上一共有 2^n×2^n 个顶点(包括左下角起点和右下角终点),恰好覆盖每个方格一次。
H_n(n > 1)可以通过如下方法构造:
-
将 H_n-1 顺时针旋转 90 度放在左下角;
-
将 H_n-1 逆时针旋转 90 度放在右下角;
-
将 2 个 H_n-1 分别放在左上角和右上角;
-
用 3 条单位线段把 4 部分连接起来。
对于 H_n 上每一个顶点 p ,我们定义 p 的坐标是它覆盖的小方格在矩阵中的坐标(左下角是(1, 1),右上角是(2^n, 2^n),从左到右是 X 轴正方向,从下到上是 Y 轴正方向),定义 p 的序号是它在曲线上从起点开始数第几个顶点(从 1 开始计数)。
以下程序对于给定的 n (n<=30) 和 p 点坐标 (x, y),输出 p 点的序号。
请仔细阅读分析源码,填写划线部分缺失的内容。
源代码
C
#include <stdio.h>
long long f(int n, int x, int y) {
if (n == 0) return 1;
int m = 1 << (n - 1);
if (x <= m && y <= m) {
return f(n - 1, y, x);
}
if (x > m && y <= m) {
return 3LL * m * m + f(n - 1, ________________ , m * 2 - x + 1); // 填空
}
if (x <= m && y > m) {
return 1LL * m * m + f(n - 1, x, y - m);
}
if (x > m && y > m) {
return 2LL * m * m + f(n - 1, x - m, y - m);
}
}
int main() {
int n, x, y;
scanf("%d %d %d", &n, &x, &y);
printf("%lld", f(n, x, y));
return 0;
}
Java
import java.util.Scanner;
public class Main {
public static long f(int n, int x, int y) {
if (n == 0) return 1;
int m = 1 << (n - 1);
if (x <= m && y <= m) {
return f(n - 1, y, x);
}
if (x > m && y <= m) {
return 3L * m * m + f(n - 1, ________________ , m * 2 - x + 1); //填空
}
if (x <= m && y > m) {
return 1L * m * m + f(n - 1, x, y - m);
}
if (x > m && y > m) {
return 2L * m * m + f(n - 1, x - m, y - m);
}
return -1;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int x = in.nextInt();
int y = in.nextInt();
System.out.println(f(n, x, y));
}
}
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:首先分析可知,每一个n阶图形,都是由4个n-1阶经过变化拼接后得到的,那么很显然的是,如下的代码段:
int m = 1 << (n - 1);
if (x <= m && y <= m) {
return f(n - 1, y, x);
}
if (x > m && y <= m) {
return 3LL * m * m + f(n - 1, ________________ , m * 2 - x + 1); // 填空
}
if (x <= m && y > m) {
return 1LL * m * m + f(n - 1, x, y - m);
}
if (x > m && y > m) {
return 2LL * m * m + f(n - 1, x - m, y - m);
}
就是在判断我们要求的点处在哪一个子块中,然后不断递归求解。
举例来说,当我们发现处于第i块个n阶子图时,前面有完整的n-1阶子块,每一个n-1阶子块的路径长度为2^(n-1)*2^(n-1)。
这边解释一下路径长度:实际上,我们看图可以知道,单独一个1阶块是长度是3,但是它作为2阶块的子块时,长度是4(多了一条块与块之间的连接线),高阶同理。
现在我们明白:f(n,x,y)表明的是在n阶图形中,(x,y)图形起点的长度。
从图中我们可以看出,在2阶图中,左上和右上两块和1阶图保持一直,左下逆时针旋转90°得到1阶图,右下顺时针旋转90°得到一阶图,3阶与2阶的关系也如此。
因此,当我们降阶时,还需要做相应的坐标转换。
我们需要求的空白处对应的是右下这种情况,也就是顺时针旋转的坐标变化。直接考虑转换公式是很难想的,我们可以找一些不变的基准或者是一些变换简单的基准,计算与基准的差异,借助基准的简单变换进行转移:
当图形顺时针旋转90°后,可以发现,左基准变成了上基准,上基准变成了右基准,因此对于每一个点,变换前我们求与左、上基准的距离,变化后使用新基准变换回来即可。
对于右下图形中的点(x,y),相对于左基准的距离为x-(m+1),相对于上基准的距离为(m-y)。
使用基准进行变化可知:x'=m-(m-y)=y,y'=m-(x-(m+1))=2*m+1-x。
but,到这里还没结束。我们看一下变换前后的路径对应:
变换前的路径到变换后变成了右边所示,但是我们上面说过,函数f求取的是到当前阶图形起点的距离,而实际变换后对应路径变成了到终点的距离,但是,幸运的是,本处图形正好关于x=(m+1)/2这条线是对称的,一次可以通过对称变化将路径投影成为到起点的距离。因此,实际上应该有x''=(m+1)-x'=m+1-y。
#include <stdio.h>
long long f(int n, int x, int y) {
if (n == 0) return 1;
int m = 1 << (n - 1);
if (x <= m && y <= m) {
return f(n - 1, y, x);
}
if (x > m && y <= m) {
return 3LL * m * m + f(n - 1, m + 1 - y, m * 2 - x + 1); // 填空
}
if (x <= m && y > m) {
return 1LL * m * m + f(n - 1, x, y - m);
}
if (x > m && y > m) {
return 2LL * m * m + f(n - 1, x - m, y - m);
}
}
int main() {
int n, x, y;
scanf("%d %d %d", &n, &x, &y);
printf("%lld", f(n, x, y));
return 0;
}
四、发现环
题目描述
小明的实验室有 N 台电脑,编号 1⋯N。原本这 N 台电脑之间有 N−1 条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了 BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入描述
输入范围:
第一行包含一个整数 N 。
以下 N 行每行两个整数 a,b,表示 a 和 b 之间有一条数据链接相连。
其中, 1≤N≤10^5,1≤a,b≤N。
输入保证合法。
输出描述
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
输入输出样例
示例
输入
5
1 2
3 1
2 4
2 5
5 3
输出
1 2 3 5
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:对于环路,其特点为从点X出发,还可以再次到达点X。判环很显然可以用遍历整幅图的方式,分为DFS与BFS。但是要求环上的元素,我们需要更变一下我们的思路:
1.DFS+栈
我们用栈存下每一次DFS遇到的元素,如果遇到了已经在栈内的元素x,那么直接从栈弹元素直到x,期间所有的元素包括x都是在环上的。
#include <iostream>
#include <stack>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 7;
int Head[maxn], To[maxn], Next[maxn], cnt;
bool inStack[maxn];
vector<int> inCircle;
void add(int from, int to)
{
To[++cnt] = to;
Next[cnt] = Head[from];
Head[from] = cnt;
}
//树上增加一条边形成的环只可能有一个
bool DFS(int p, int pre, stack<int>& st)
{
int q;
for(int e = Head[p]; e != 0; e = Next[e])
{
q = To[e];
//cout << p << "-" << q << endl;
if(q == pre) continue;
if(inStack[q])
{
while(!st.empty() && st.top() != q)
{
inCircle.push_back(st.top());
st.pop();
}
inCircle.push_back(q);
return true;
}
st.push(q);
inStack[q] = true;
if(DFS(q, p, st))//找到了环
{
return true;
}
st.pop();
inStack[q] = false;
}
return false;
}
int main()
{
int N, a, b;
stack<int> st;
cin >> N;
for(int i = 1; i <= N; ++ i)
{
cin >> a >> b;
add(a, b);
add(b, a);
}
st.push(1);
inStack[1] = true;
DFS(1, -1, st);
sort(inCircle.begin(), inCircle.end());
for(auto p : inCircle)
{
cout << p << " ";
}
return 0;
}
2.BFS+拓扑排序
对于存在环的情况,我们可以知道拓扑排序后一定会存在遗漏点。现在需要解决的是如何在无向图上进行拓扑排序。
对于本题的情况,首先我们可以明确,本题是一棵树上多了一条边导致生成的环,因此生成的环一定是一个简单环路,即点与点之间首尾相连。
在无向图中,点无法区分入度和出度,或者说 入度=出度=度数。对于这样的简单环路,显而易见该环上的点的度数都是2,而度数为1的点。但需要注意的是,入度为2的点不一定是环中的点:
如上图,根节点的度数为2,但并不是环中的点。我们来讨论一下如何在无向图中使用拓扑排序算法:
1.找到所有度数为1的点,入队列。
2.从队头拿到元素x,将x的所有邻接点的度数减1,若度数减为1,则表明这不是环中的点,入队列备用;但必须知道,如果当前度数没有减为1,也并不代表它一定是环路中的点。
3.重复步骤2直到队列为空,此时遍历所有点的度数,如果度数为2,则必定是环中的点。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
int degree[maxn];
bool visited[maxn];
vector<int> arc[maxn];
void BFS(queue<int>& points)
{
int p, i, to;
while(!points.empty())
{
p = points.front();
points.pop();
for(i = arc[p].size() - 1; i >= 0; -- i)
{
to = arc[p][i];
if(visited[to])
{
continue;
}
-- degree[to];
if(degree[to] == 1)
{
visited[to] = true;
points.push(to);
}
}
}
}
int main()
{
int N, a, b, i;
queue<int> points;
cin >> N;
for(i = 1; i <= N; ++ i)
{
cin >> a >> b;
degree[a] += 1;
degree[b] += 1;
arc[a].push_back(b);
arc[b].push_back(a);
}
for(i = 1; i <= N; ++ i)
{
if(degree[i] == 1)
{
degree[i] = 0;
points.push(i);
visited[i] = true;
}
}
BFS(points);
for(i = 1; i <= N; ++ i)
{
if(degree[i] == 2)
{
cout << i << " ";
}
}
}
五、对局匹配
题目描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是 K 的两名用户匹配在一起。如果两人分差小于或大于 K,系统都不会将他们匹配。
现在小明知道这个网站总共有 N 名用户,以及他们的积分分别是 A1,A2,⋯AN。
小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于 K)?
输入描述
输入描述
第一行包含两个个整数 N,K。
第二行包含 N 个整数 A1,A2,⋯AN。
其中,1≤N≤10^5,0≤Ai≤10^5,0≤K≤10^5。
输出描述
输出一个整数,代表答案。
输入输出样例
示例
输入
10 0
1 4 2 8 5 7 1 4 2 8
输出
6
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
思路:很显然,当k=0时,答案就是数据中存在多少不同的数。
当k>0的时候,我们发现,实际上,只有那些对k同余的才可能会产生影响。如k=3,那么实际上会互相影响的举例为: 0 3 6 9、2 5 8 11等。那么我们可以分组考虑,即对这k组分别求最大的不匹配数,求和即可。
用dp[i]表示在0~i区间中,与i同组的最大不匹配数,那么很显然,对于i来说:
A.如果选择i,那么不能选择i-k,即dp[i] = num[i] + dp[i - 2 * k] (num[i]表示数值为i的个数)
B.如果不选择i,那么dp[i] = dp[i - k]
#include <iostream>
#include <algorithm>
#include <unordered_set>
using namespace std;
const int maxn = 1e5 + 7;
int dp[maxn];
int main()
{
int N, k, i, score, maxScore = 0, ans = 0;
cin >> N >> k;
if(k == 0)
{
unordered_set<int> scores;
for(i = 1; i <= N; ++ i)
{
cin >> score;
scores.insert(score);
}
cout << scores.size() << endl;
return 0;
}
for(i = 1; i <= N; ++ i)
{
cin >> score;
maxScore = max(maxScore, score);
++ dp[score]; //直接用计数对dp初始化 省去num数组
}
for(i = k; i <= maxScore; ++ i)
{
if(i - k * 2 >= 0)
{
dp[i] = max(dp[i] + dp[i - k * 2], dp[i - k]);
}
else
{
dp[i] = max(dp[i - k], dp[i]);
}
}
for(i = 0; i < k; ++ i)
{
ans += dp[maxScore - i];
}
cout << ans;
return 0;
}
思路拓展:实际上本题还可以二分不匹配数,然后使用DFS去判断是否合法。(不是100%的解法)
六、铁路观光
题目描述
跳蚤国正在大力发展旅游业,每个城市都被打造成了旅游景点。
许多跳蚤想去其他城市旅游,但是由于跳得比较慢,它们的愿望难以实现。这时,小 C 听说有一种叫做火车的交通工具,在铁路上跑得很快,便抓住了商机,创立了一家铁路公司,向跳蚤国王请示在每两个城市之间都修建铁路。
然而,由于小 C 不会扳道岔,火车到一个城市以后只能保证不原路返回,而会随机等概率地驶向与这个城市有铁路连接的另外一个城市。
跳蚤国王向广大居民征求意见,结果跳蚤们不太满意,因为这样修建铁路以后有可能只游览了 3 个城市(含出发的城市)以后就回来了,它们希望能多游览几个城市。于是跳蚤国王要求小 C 提供一个方案,使得每只跳蚤坐上火车后能多游览几个城市才回来。
小 C 提供了一种方案给跳蚤国王。跳蚤国王想知道这个方案中每个城市的居民旅游的期望时间(设火车经过每段铁路的时间都为 1 ),请你来帮跳蚤国王。
输入描述
输入的第一行包含两个正整数 n、m,其中 n 表示城市的数量,m 表示方案中的铁路条数。
接下来 m 行,每行包含两个正整数u、v,表示方案中城市 u 和城市 v 之间有一条铁路。
保证方案中无重边无自环,每两个城市之间都能经过铁路直接或间接到达,且火车由任意一条铁路到任意一个城市以后一定有路可走。
其中,4≤k≤n≤21,1≤u,v≤n。
输出描述
输出 n 行,第 i 行包含一个实数 B_i,表示方案 B 中城市 i 的居民旅游的期望时间。
你应当输出足够多的小数位数,以保证输出的值和真实值之间的绝对或相对误差不超过 10^−9。
输入输出样例
示例
输入
4 5
1 2
2 3
3 4
4 1
1 3
输出
3.333333333333
5.000000000000
3.333333333333
5.000000000000
运行限制
- 最大运行时间:2s
- 最大运行内存: 256M
[注]:自环指的是从x有一条直接到x的边。
思路:本题可能会有人觉得只要求出某一个点X出发并回来的所有路径数,就可以求得每一个路径的概率,也由此可以知道期望,但问题是本题并没有说每一个城市只能经过一次,而一旦存在环路,就会导致路线无限延长,即存在无穷可列个情况,但要明白,无穷可列个情况并不一定会使得期望不存在(详见概率论),因此这个方法不现实。但我们退而求其次,本题允许的误差是1e-9,那么是否可以说明路径到了一定的长度后,误差在这个范围内趋于稳定,这是必然的(无穷级数理论),也就是说整个求和结果变得可知,结果是一个无限逼近的结果,即有上限。但问题在于,枚举到何种程度时误差可控不可知,且这样做,枚举的路径长度一定会比较长,此时复杂度会很高,因此也不会是正解。
[注]:在某些数据下,DFS不考虑环应该是可以过一些特别小的数据的,即一些小图的情况。
本题实际上是一个类流量均衡问题,我们将路径被经过的概率想象成流量,当流量趋于稳定时,对于城市x,只要求其邻接边的流量和即是城市被访问的次数的期望。
1.初始化的时候,x的度数为deg[x],那么x的所有邻接边上的流量即为 1 / deg[x]。即若用p[x][y]表示x->y的流量,则有p[x][y] = 1 / deg[x]。
2.定义in[y]表示流入y的流量,则有:
然后。。。还是放郑未老师的题解叭QAQ
解题思路
这个解法是满分解法,对每个点单独计算。
设这个点的度为 d,那么它向外的每条边都有 概率被走。
我们对每条边定义一个有向权值 P(u,v),含义为这条边被走的次数的期望。显然起始点的各边的外向权值为 。
接下来我们考虑把权值往外进一步扩散。
由于单纯的 BFS/DFS 复杂度高,边界条件不清楚,因此不宜采用这类写法。
由于图中的权值有马尔可夫性(简单理解:无记忆性),确定存在解的话,我们可以迭代得到稳定状态。
这里的权值类似于网络流中的流量,对每个结点需要做到流量平衡。
算法描述:
in(v) 表示 vv 的总入权,入权可以流向相邻边
- 外层:确认源点
初始化源点到相邻点的P值
-
第二层:对每一条边,反复更新 v 的入权和 P(u,v),直到更新差异很小。
-
遍历每条边,根据 P(u,v) 更新 in(v): in(v)+=P(u,v);
-
此时,每个点的 in 值可能发生变化,这将引起其对外 PP 值的变化。对于 u 来说,其入权有一部分是来自 e 的 流入,扣除后可分配权值为 in(u)−P(v,u) 。
-
当前 u 流入的权值,除去经由 e 流入的之外,均可由 e 流出(e是其中的)。若 u 的度为 deg(u),e 将分得其中的 。
因此,设当前 u 的总流入权值为 ,则任意 的权值 P(u, v) 需被更新为 。注意权值只向外扩,所以当计算的更新值小于当前值时不更新。
继续迭代……
当全图权值均不变化时停止迭代,此时权值和 ∑P(u,v) 即为答案。
注意图规模越大时需要的迭代次数越多,但迭代时间越长,这里需要做一定的权衡。
#include <vector>
#include <iostream>
#define eps 1e-10
#define maxn 25
using namespace std;
int deg[maxn];
vector<int> G[maxn];
double in[maxn];
double P[maxn][maxn];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= m; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
++deg[v], ++deg[u]; // calculate degree
}
for (int uu = 1; uu <= n; ++uu) { // 对每个顶点
memset(P, 0, sizeof P);
for (size_t j = 0; j < G[uu].size(); ++j) // 遍历邻居
{
int v = G[uu][j];
P[uu][v] = 1.0 / deg[uu];
printf("P[%d][%d]=%f\n", uu, v, P[uu][v]);
} // 初始化P[u][v],源点到相邻点的P值确定
bool flag = true;
int T = max(10000, 1000000 / m); // determine number of iterations
while (flag || T--) { // 控制总迭代次数
flag = false;
for (int i = 1; i <= n; ++i) in[i] = 0.0;
// 更新入权:根据P值更新in值(即某点的总入权)
for (int u = 1; u <= n; ++u) {
for (size_t j = 0; j < G[u].size(); ++j) {
int v = G[u][j];
// 增加v的入权,即增加来自当前边的P[u][v]
in[v] += P[u][v]; // calculate in for each vertex
printf("in[%d] = %f \n", v, in[v]);
}
}
for (int u = 1; u <= n; ++u) {
for (size_t j = 0; j < G[u].size(); ++j) {
int v = G[u][j];
// 可分配到uv的权值
double diff = (in[u] - P[v][u]) / (deg[u] - 1) - P[u][v]; // update diff
if (diff > 0) { // update only if increasing
P[u][v] += diff;
printf("P[%d][%d] = %f \n",u, v, P[u][v]);
if (diff > 1e-15)
flag = true; // 意味着还需要更新
}
}
}
}// 迭代完成
double ans = 0.0;
for (int u = 1; u <= n; ++u) {
for (size_t i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
ans += P[u][v];
}
}// ans为所有边的权之和
printf("%.12lf\n", ans);
}
return 0;
}