进阶指南_图论_lduoj_做题记录

A. 最优贸易

Description

C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 1 条。
C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 C 国 n 个城市的标号从 1∼n,阿龙决定从 1 号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有 n 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
假设 C 国有 5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。

图片来自LDUOJ

假设 1∼n 号城市的水晶球价格分别为 4,3,5,6,1。

阿龙可以选择如下一条线路:1->2->3->5,并在 2 号城市以 3 的价格买入水晶球,在 3号城市以 5 的价格卖出水晶球,赚取的旅费数为 2。

阿龙也可以选择如下一条线路 1->4->5->4->5,并在第 1 次到达 5 号城市时以 1 的价格买入水晶球,在第 2 次到达 4 号城市时以 6 的价格卖出水晶球,赚取的旅费数为 5。

现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

Input
第一行包含 2 个正整数 n 和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。
接下来 m 行,每行有 3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果 z=1,表示这条道路是城市 x 到城市 y 之间的单向道路;如果 z=2,表示这条道路为城市 x 和城市y 之间的双向道路。

1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市水晶球价格≤100。

Output
输出共 1 行,包含 1 个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出 0。

Samples
Input

5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2

Output

5

大致方法:
看懂题目意思之后可以知道有多种状态,买和卖的关系,这样就可以用分层图最短路。
在建边的时候,假设建立多层,对于本题,首先要建立原始的一层,然后有买的一层,然后还要有卖的一层,这个时候就是要建立三层
这个题来讲,对于两点的关系:在同一层之间建立边权为0的关系
在另外的两层中,同层之间也是建立边权为0的边。在两层之间建立边权为正的一条边,在另外两层之间建立边权为负数的边
在建立关系的过程中,要在相邻的层之间建立

void _addEdge(int x,int y){
    add(x,y,0);/// val == 0
    add(x+n,y+n,0);/// another floor
    add(x+2*n,y+2*n,0);/// another floor
    add(x,y+n,-1 * val[x]);/// between two floor  +
    add(x+n,y+2*n,val[x]);/// between two floor -
}

const int maxn = 1e6+7;
int n;
int m;
int val[maxn << 1];
int head[maxn << 1];
bool vis[maxn << 1];
int cnt = 0;
struct node {
    int u,v,w;
    int nex;
} e[maxn];
int dis[maxn << 1];
void add(int u,int v,int w)
{
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
void init()
{
    for(int i=1; i<=3*n; i++) {
        head[i] = -1;
        dis[i] = -999999;
        vis[i] = 0;
    }
}
void _addEdge(int x,int y){
    add(x,y,0);/// val == 0
    add(x+n,y+n,0);/// another floor
    add(x+2*n,y+2*n,0);/// another floor
    add(x,y+n,-1 * val[x]);/// between two floor  +
    add(x+n,y+2*n,val[x]);/// between two floor -
}
/// <
struct cmp{
    bool operator()(int a,int b)
    {
        return dis[a] < dis[b];
    }
};
void spfa(int x){
    queue<int> que;
    que.push(x);
    vis[x] = 1;dis[x] = 0;/// visit the pnt
    while(que.size()){
        int u = que.front();que.pop();
        vis[u] = false;
        for(int i=head[u];~i;i = e[i].nex){
            int v = e[i].v;
            if(dis[v] < dis[u] + e[i].w){
                dis[v] = dis[u] + e[i].w;
                if(vis[v] == 0){
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
}
int main()
{
    n = read,m=read;
    init();
    for(int i=1;i<=n;i++) val[i] = read;
    for(int i=1;i<=m;i++){
        int u=read,v=read,op=read;
        _addEdge(u,v);
        if(op == 2) _addEdge(v,u);
    }
    ///add(n,2*n,0),add(2*n,3*n,0);
    spfa(1);
    cout<<max(dis[n],dis[3*n])<<'\n';
    return 0;
}

B. 道路和航线

Description

Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到T
个城镇 (1≤T≤25,000),编号为1T。这些城镇之间通过R条道路 (1≤R≤50,000,编号为1到R) 和P条航线 (1≤P≤50,000,编号为1到P) 连接。每条道路i或者航线i连接城镇Ai (1≤Ai≤T)到Bi (1≤Bi≤T),花费为Ci。对于道路,0≤Ci≤10,000; 然而航线的花费很神奇,花费Ci可能是负数(−10,000≤Ci≤10,000)。道路是双向的,可以从Ai到Bi,也可以从Bi到Ai,花费都是Ci。然而航线与之不同,只可以从Ai到Bi。事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台 了一些政策保证:如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。由于FJ的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。他想找到从发送中心城镇S(1≤S≤T) 把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。

Input

第1行:四个空格隔开的整数: T,R,P, and S

第2到R+1行:三个空格隔开的整数(表示一条道路):Ai,Bi 和 Ci
第R+2
到R+P+1行:三个空格隔开的整数(表示一条航线):Ai,Bi 和 Ci
Output
第1到T行:从S到达城镇i的最小花费,如果不存在输出"NO PATH"。
Samples
Input 复制

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

Output

NO PATH
NO PATH
5
0
-95
-100

Hint

一共六个城镇。在1-2,3-4,5-6之间有道路,花费分别是5,5,10。同时有三条航线:3->5,
4->6和1->3,花费分别是-100,-100,-10。FJ的中心城镇在城镇4。
FJ的奶牛从4号城镇开始,可以通过道路到达3号城镇。然后他们会通过航线达到5和6号城镇。
但是不可能到达1和2号城镇。

通过题意可以很轻松的了解到这是个最短路问题
因为存在负数,所以要注意负环的问题,但是题目中说到事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台 了一些政策保证:如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai,所以就不存在负环的现象

但是如果用单纯的SPFA会被卡TLE,这个时候要用到SPFA的双端队列优化或者是尝试其他方法,这里采用SPFA的双端队列优化

如果说当前节点不在队列里面,并且这个点的最短距离小于队列头端点的距离,就可以放到队列的前端,否则就放到队列的末端

const int maxn = 1e6+7;
int t,r,p,s;
struct node{
    int u;
    int v;
    int nex;
    int w;
}e[maxn];
int cnt = 0;
int head[maxn];
int vis[maxn];
int dis[maxn];
void init(){
    for(int i=1;i<=(t<<1);i++) {
        vis[i] = 0;
        dis[i] = 0x3f3f3f3f;
        head[i] = -1;
    }
}
void add(int x,int y,int w){
    e[cnt].u = x;
    e[cnt].v = y;
    e[cnt].w = w;
    e[cnt].nex = head[x];
    head[x] = cnt ++;
}
void spfa(int x){
    ///queue<int> que;
    deque<int>que;
    que.push_back(x);
    dis[x] = 0;
    /// vis[x] = 1;
    while(que.size()){
        int u = que.front();
        que.pop_front();
        vis[u] = 0;
        for(int i=head[u];~i;i = e[i].nex){
            int to = e[i].v;
            if(dis[to] > dis[u] + e[i].w){
                dis[to] = dis[u] + e[i].w;
                if(!vis[to]){
                    vis[to] = 1;
                    ///que.push(to);
                    if(que.size() && dis[to] < dis[que.front()]) que.push_front(to);
                    else que.push_back(to);
                }
            }
        }
    }
}
int main()
{
   /// freopen("1.in","r",stdin);
    cin >> t >> r >> p >> s;
    init();
    for(int i=1;i<=r;i++){
        int x=read,y=read,w=read;
        add(x,y,w);
        add(y,x,w);
    }
    for(int i=1;i<=p;i++){
        int x=read,y=read,w=read;
        add(x,y,w);
    }
    /// puts("ok");
    spfa(s);
    for(int i=1;i<=t;i++){
        if(dis[i] == 0x3f3f3f3f) puts("NO PATH");
        else printf("%d\n",dis[i]);
    }
    return 0;
}

D. Sorting It All Out

Description

An ascending sorted sequence of distinct values is one in which some form of a less-than operator is used to order the elements from smallest to largest. For example, the sorted sequence A,B,C,D implies that A<B, B<C and C<D. in this problem, we will give you a set of relations of the form A<Band ask you to determine whether a sorted order has been specified or not.
Input

Input consists of multiple problem instances. Each instance starts with a line containing two positive integers n
and m. the first value indicated the number of objects to sort, where 2≤n≤26. The objects to be sorted will be the first n characters of the uppercase alphabet. The second value m indicates the number of relations of the form A<B which will be given in this problem instance. Next will be m lines, each containing one such relation consisting of three characters: an uppercase letter, the character “<” and a second uppercase letter. No letter will be outside the range of the first n letters of the alphabet. Values of n=m=0 indicate end of input.
Output

For each problem instance, output consists of one line. This line should be one of the following three:
Sorted sequence determined after xxx relations: yyy…y. Sorted sequence cannot be determined. Inconsistency found after xxx relations. where xxx is the number of relations processed at the time either a sorted sequence is determined or an inconsistency is found, whichever comes first, and yyy…y is the sorted, ascending sequence.

Samples
Input 复制

4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0

Output

Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.

考虑拓扑排序,有非常明显的从局部的特征到整体的特征之间的特点,拓扑排序可以跳转博客
因为数据范围足够小,并且题目要求输出循环次数,说明跑拓扑排序是完全没问题的
当入度为0的点有多个的情况下,可以得到答案不唯一的结果,当vector中的点的数量小于n的情况下,说明此时有1环,即有冲突,当以上两种情况均在循环中没有发生,即得不到答案

const int maxn = 1e6+7;
int n,m;
vector<int> vet;
int a[30][30];
int deg[30];
int teg[30];
string s[30000];
int topSort(){
    queue<int> que;
    int cnt = 0;
    for(int i=1;i<=n;i++){
        if(deg[i] == 0) que.push(i);
        teg[i] = deg[i];
    }
    vet.clear();
    int flag = 0;

    while(que.size()){
        if(que.size() > 1) flag = 1;
        int top = que.front();
        que.pop();
        int tot = 0;
        vet.push_back(top);
        for(int i=1;i<=n;i++){
            if(a[top][i]){
                teg[i] --;
                if(teg[i] == 0){
                    que.push(i);
                }
            }
        }
    }
    if(vet.size() < n) return 0;///huan
    if(flag == 1) return 1;///
    return 2;
}
int main()
{
    ///freopen("1.in","r",stdin);
    while(cin >> n >> m){
        if(n == 0 && m == 0) break;
        for(int i=1;i<=m;i++) cin >> s[i];
        int flag = 0;
        int siz;
        memset(a,0,sizeof a);
        memset(deg,0,sizeof deg);
        for(int i=1;i<=m;i++){
            int u = s[i][0];
            int v = s[i][2];
            u -= 'A'; u ++;
            v -= 'A'; v ++;
            deg[v] ++;
            a[u][v] = 1;
            siz = topSort();
            if(siz == 2){
                printf("Sorted sequence determined after %d relations: ",i);
                for(int i=0;i<n;i++) printf("%c",vet[i] + 'A' - 1);
                puts(".");
                flag = 1;
                break;
            }
            else if(siz == 0){
                printf("Inconsistency found after %d relations.\n",i);
                flag = 1;
                break;
            }
        }
        if(flag == 0) puts("Sorted sequence cannot be determined.");
    }
    return 0;
}

F. 走廊泼水节

Description

给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。求增加的边的权值总和最小是多少。我们一共有N个OIER打算参加这个泼水节,同时很凑巧的是正好有N个水龙头(至于为什么,我不解释)。N个水龙头之间正好有N−1条小道,并且每个水龙头都可以经过小道到达其他水龙头(这是一棵树,你应该懂的…)。但是OIER门为了迎接中中的挑战,决定修建一些个道路(至于怎么修,秘密~),使得每个水龙头到每个水龙头之间都有一条直接的道路连接(也就是构成一个完全图呗~)。但是OIER门很懒得,并且记性也不好,他们只会去走那N-1条小道,并且希望所有水龙头之间修建的道路,都要大于两个水龙头之前连接的所有小道(小道当然要是最短的了)。所以神COW们,帮那些OIER们计算一下吧,修建的那些道路总长度最短是多少,毕竟修建道路是要破费的~

Input
多组数据,第一行t,表示有t组测试数据,对于每组数据:第一行N,表示水龙头的个数(当然也是OIER的个数);
2到N行,每行三个整数X,Y,Z;表示水龙头X和水龙头Y有一条长度为Z的小道
Output
对于每组数据,输出一个整数,表示修建的所有道路总长度的最短值。
Samples
Input

2
3
1 2 2
1 3 3
4
1 2 3
2 3 4
3 4 5

Output

4
17

Hint

第一组数据,在2和3之间修建一条长度为4的道路,是这棵树变成一个完全图,且原来的树依然是这个图的唯一最小生成树.
每个测试点最多10组测试数据
50%: n≤1500;
100%: n≤6000
100%: z≤100

克鲁斯卡尔思想,分享一片比较好的博文
要用到并查集维护联通块,如果说再给两个本来不连通的连通块连上一条边之后,增加的边的数量是siz_a * siz_b - 1;
因为还不能破坏之前存在的最小生成树(假设边权为w),那么来说新增加的边权至少是w + 1;

const int maxn = 5e5 + 7;
const double eps = 1e-6;
int n, m;
int T;
struct node
{
    int u, v, w;
} e[maxn];
bool cmp(node a, node b)
{
    return a.w < b.w;
}
int fa[maxn];
int find(int x)
{
    if(x == fa[x]) return x;
    else return fa[x] = find(fa[x]);
}
int siz[maxn];
void init()
{
    for(int i = 1; i <= n; i++)
    {
        fa[i] = i;
        siz[i] = 1;
    }
}

int main()
{
    cin >> T;
    while(T --)
    {
        n = read;
        init();
        for(int i = 1; i < n; i++)
        {
            e[i].u = read, e[i].v = read, e[i].w = read;
        }
        ll ans = 0;
        sort(e + 1, e + n, cmp);
        for(int i = 1; i < n; i++)
        {
            int fau = find(e[i].u);
            int fav = find(e[i].v);
            if(fau == fav) continue;
            else
            {
                fa[fau] = fav;
                ans += (e[i].w + 1) * (siz[fau] * siz[fav] - 1);
                siz[fav] += siz[fau];
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

G. 黑暗城堡

Description

在顺利攻破Lord lsp的防线之后,lqr一行人来到了Lord lsp的城堡下方。Lord lsp黑化之后虽然拥有了强大的超能力,能够用意念力制造建筑物,但是智商水平却没怎么增加。现在lqr已经搞清楚黑暗城堡有N
个房间 (1≤N≤1000),M条可以制造的双向通道,以及每条通道的长度。
lqr深知Lord lsp的想法,为了避免每次都要琢磨两个房间之间的最短路径,Lord lsp一定会把城堡修建成树形的;但是,为了尽量提高自己的移动效率,Lord lsp一定会使得城堡满足下面的条件:设 D[i] 为如果所有的通道都被修建,第 i 号房间与第1号房间的最短路径长度;而 S[i] 为实际修建的树形城堡中第 i 号房间与第1号房间的路径长度;要求对于所有整数 i(1≤i≤N),有 S[i]=D[i] 成立。
为了打败Lord lsp,lqr想知道有多少种不同的城堡修建方案。于是lqr向applepi提出了这个问题。因为applepi还要忙着出模拟赛,所以这个任务就交给你了。当然,你只需要输出答案对 231–1取模之后的结果就行了。

Input

第一行有两个整数N和M。
之后M 行,每行三个整数X,Y 和L,表示可以修建X 和Y之间的一条长度为L 的通道。
Output

一个整数,表示答案对 231–1

取模之后的结果。
Samples
Input 复制

3 3
1 2 2
1 3 1
2 3 1

Output

2

Hint

对于30% 的数据,2≤N≤5,M≤10。
对于50% 的数据,满足条件的方案数不超过10000。
对于100% 的数据,2≤N≤1000,N–1≤M≤N(N–1)/2,1≤L≤100

n的数据范围比较小,n 2 也是可以的,dijstra(),求出来最短路之后,可以直接使用乘法原理加取膜进行操作就好

const int maxn = 1e6+7;
struct node{
    int v;
    int w;
    int nex;
}e[maxn];
bool vis[maxn];
ll dis[maxn];
int head[maxn];
int cnt = 0;
int n,m;
typedef pair<int,int> PII;
void init(){
    for(int i=1;i<=n;i++){
        dis[i] = 0x3f3f3f3f;
        head[i] = -1;
        vis[i] = 0;
    }
}
void add(int u,int v,int w){
    e[cnt].v = v;
    e[cnt].nex = head[u];
    e[cnt].w = w;
    head[u] = cnt++;
}
void Dijkstra(int x){
    dis[x] = 0;
    priority_queue<PII,vector<PII>,greater<PII> > que;
    que.push({dis[x],x});
    while(que.size()){
        PII cur = que.top();
        que.pop();
        int u = cur.second;
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i=head[u];~i;i = e[i].nex){
            int v = e[i].v;
            int w = e[i].w;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                que.push({dis[v],v});
            }
        }
    }
}

ll ans = 1;
const ll mod = (1L << 31) - 1L;
int main()
{
    cin >>n >> m;
    init();
    for(int i = 1;i<=m;i++){
        int u,v,w; cin >> u >> v >> w;
        add(u,v,w);
        add(v,u,w);
    }
    Dijkstra(1);
    for(int i=2;i<=n;i++){
        int cnt = 0;
        for(int j = head[i];~j;j = e[j].nex){
            int v = e[j].v;
            int w = e[j].w;
            if(dis[i] == dis[v] + w){
                cnt ++;
            }
        }
        ans = (ans * cnt) % mod;
        ans %= mod;
    }
    cout<<ans<<'\n';
    return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值