图的构成有各种各样的方式,只要你认为逻辑上能形成一张图,并且你看得懂自己的构图数据,那么就是一张好图。。(这只是我的一点认为)
为了打ACM不得不使用简洁,效率高的代码(说白了就是赶时间) 下面总结几种方法
第一种 也是目前比较流行的 邻接表形式
/**********************************************************************************************
邻接表法:每个节点i都有一个链表,里面保存着从i出发的所有边
Edge是边的信息
首先:e[i] 代表了 第i条边
1 . to是它指向的那个点
2 . w代表这条边的权值
3 . next代表与这个点相连的下一条边的序号(这里的“下一条”边其实是先输入的边呵呵)
head
head[i]记录的是第i个节点发出的边的序号
************************************************************************************************/
struct Edge{
int to;
int w;
int next;
}e[MAX];
int head[MAX];
//初始化 因为各个节点没有发出边,所以head数组置为 -1
memset(head,-1,sizeof(head));
//输入过程(建图过程)
scanf("%d %d %d", &from, &to, &w1);
e[i].to = to;
e[i].w = w1;
e[i].next = head[from];
head[from] = i;
i++;
//遍历某个节点发出的所有边 now表示现在的节点
for(i = head[now]; i != -1; i = edge[i].next)
{
int adj = edge[i].to;
int w = edge[i].w;
//do something...
}
第二种 尽管第一种邻接表很流行,但在概念上却没有第二种 —— 用vector数组更加简单
struct Edge{
int from, to, w;
Edge(int u, int v, int d):from(u), to(v), w(d){}
};
vector<Edge> edges;
vector<int> G[MAX];
scanf("%d %d %d", &u, &v, &w1);
edges.push_back(Edge(u, v, w1));
edges.push_back(Edge(v, u, w1)); //如果是无向图,还应该反向添加一次
int c = edge.size(); //c可以代表总的边的条数,而条数等于边的序号,即第i条边的序号为i
G[u].push_back(c - 2);
G[v].push_back(c - 1);
//若想遍历与某点x相关联的边
for(int i = 0; i < G[x].size(); i++){
//do something...
}
第三种:而vector里存的东西也不一定要是直接储存边Edge 也可以是与这个点相连的边的编号(该点作为边前端。。)
下面上代码
struct Edge{
int u, v;
Edge(int u = 0, int v = 0):u(u),v(v){};
}e[MAX];
vector<int> G[MAX];
//G[i]储存的是与第i个点相连的所有边的序号
//构图过程
int cnt = 0;
scanf("%d %d", &u, &v);
e[++cnt].Edge(u, v);
<pre name="code" class="cpp">G[u].push_back(cnt);
e[++cnt].Edge(v, u);//如果是无向或需要构建反向图G[v].push_back(cnt);
//遍历过程
for(int i = 0; i < G[x].size(); i++){ int tmp = e[G[x][i]].v;//取出与x相连的点的序号,然后可以对点进行一系列操作 //do somethings...}
第四种:以上都是保存边的信息,然而用vector来保存点的信息也是不错的
struct Node
{
int to; //与这个点相连的点
int val; //以这个点射出的边的权值之类
int rev; //这个点的反向边
};
vector<Node> v[MAX];
//添加各个点
void add_node(int from, int to, int cap)
{
v[from].push_back((Node){to, cap, v[to].size()});
v[to].push_back((Node){from, 0, v[from].size() - 1});//这种方式储存反向边,是为了便于查找
//也就是说如果要取用与u发出的那条边的反向边,只要v[u.to][u.rev]...
}
//如要改变点中的信息去取某个点的时候可以用引用的方式 例 Node &tmp = v[u][i]....
int main()
{
//.....
}
其他的矩阵就不再写了把,,一个二维数组就ok 就是空间复杂度实在是太高 而vector的时间效率也不是太好(但一般题目不会卡这一点时间)