第七周 Week7-图和树的性质与应用(中)

A : 卖菜

问题描述
在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜。
  第一天,每个商店都自己定了一个价格。店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自己和相邻商店的价格调整自己的价格。具体的,每家商店都会将第二天的菜价设置为自己和相邻商店第一天菜价的平均值(用去尾法取整)。
  注意,编号为1的商店只有一个相邻的商店2,编号为n的商店只有一个相邻的商店n-1,其他编号为i的商店有两个相邻的商店i-1和i+1。
  给定第一天各个商店的菜价,请计算第二天每个商店的菜价。

输入格式
输入的第一行包含一个整数n,表示商店的数量。
  第二行包含n个整数,依次表示每个商店第一天的菜价。

输出格式
输出一行,包含n个正整数,依次表示每个商店第二天的菜价。

样例输入
8
4 1 3 1 6 5 17 9
样例输出
2 2 1 3 4 9 10 13
数据规模和约定
对于所有评测用例,2 ≤ n ≤ 1000,第一天每个商店的菜价为不超过10000的正整数。

#include <iostream>
using namespace std;

int a[1001];
int b[1001];
int main()
{

int n;
cin>>n;
for(int i=1;i<=n;i++) {
    cin >> a[i];
};
for(int i=1;i<=n;i++) {
    if (i == 1) {
        b[1] = (a[1]+a[2])/2;
    } else if (i<n && i>1) {
        b[i] = (a[i - 1] +a[i]+ a[i + 1]) / 3;
    } else if (i == n) {
        b[n] =(a[n]+ a[n - 1])/2;
    }
};

for(int i=1;i<=n;i++){
    cout<<b[i]<<" ";
}

    return 0;
}

在这里插入图片描述

B : 买菜

问题描述
小H和小W来到了一条街上,两人分开买菜,他们买菜的过程可以描述为,去店里买一些菜然后去旁边的一个广场把菜装上车,两人都要买n种菜,所以也都要装n次车。具体的,对于小H来说有n个不相交的时间段[a_1,b_1][a
1

,b
1

],[a_2,b_2][a
2

,b
2

]…[a_n,b_n][a
n

,b
n

]在装车,对于小W来说有n个不相交的时间段[c_1,d_1][c
1

,d
1

],[c_2,d_2][c
2

,d
2

]…[c_n,d_n][c
n

,d
n

]在装车。其中,一个时间段[s, t]表示的是从时刻s到时刻t这段时间,时长为t-s。
  由于他们是好朋友,他们都在广场上装车的时候会聊天,他们想知道他们可以聊多长时间。

输入格式
输入的第一行包含一个正整数n,表示时间段的数量。
  接下来n行每行两个数a_ia
i

,b_ib
i

,描述小H的各个装车的时间段。
  接下来n行每行两个数c_ic
i

,d_id
i

,描述小W的各个装车的时间段。

输出格式
输出一行,一个正整数,表示两人可以聊多长时间。

样例输入
4
1 3
5 6
9 13
14 15
2 4
5 7
10 11
13 14
样例输出
3
数据规模和约定
对于所有的评测用例,1 ≤ n ≤ 2000, a_i < b_i < a_{i+1},c_i < d_i < c_{i+1}a
i <b i<a i+1,
c i <d i<c i+1,对于所有的i(1 ≤ i ≤ n)有,1 ≤ a_i, b_i, c_i, d_ia i ,b i,c i ,d i≤ 1000000。

#include<iostream>
using namespace std;
int a[10000010];
int b[10000010];
int c[10000010];
int d[10000010];

int main()
{
int n;
cin>>n;
int nn=n;
int i=0;
while(nn--)
{
cin>>a[i]>>b[i];
i++;
}
nn=n;
i=0;
while(nn--)
{
cin>>c[i]>>d[i];
i++;
}
i=0;int l=0;
int ans=0;
while(i<n&&l<n)
{

if(b[i]<=c[l])
{
	i++;
	continue;
	
}

if(d[l]<=a[i])
{
	l++;
	continue;
	
}

if(b[i]>c[l])
	if(d[l]>b[i])
	{	
	if(c[l]>a[i])
	{	ans+=b[i]-c[l];
		i++;
		continue;
	}
	else
	{
	ans+=b[i]-a[i];
	i++;
	continue;
	}
	}
	
	else 
	{
		if(a[i]<c[l])
		ans+=d[l]-c[l];
		else
		ans+=d[l]-a[i];
		l++;
		continue;
		
	}
	
if(d[l]>a[i])
	if(b[i]>d[l])
	{	if(a[i]>c[l])
		ans+=d[l]-a[i];
		else
		ans+=d[l]-c[l];
		l++;
		continue;
	}
	else 
	{
		if(a[i]>c[l])
		ans+=b[i]-a[i];
		else
		ans+=b[i]-c[i];
		i++;
		continue;
		
	}
}

cout<<ans<<endl;
} 

在这里插入图片描述

C : 穿越虫洞

问题描述
小H有nn个秘密基地(编号 11 到 nn ),nn 个秘密基地之间有 mm 条双向路径和 ww 个单向时空隧道,通过路径需要消耗一定的时间T_iT
i

,而通过时空隧道可以使时光倒流T_jT
j

,现在小H想知道他能否从某一秘密基地出发,通过路径和时空隧道回到过去(即回到出发的秘密基地且该时刻要早于出发时间)。

输入格式
第11行,一个整数 FF,表示测试用例的数量
接下来对于每一个测试用例,输入格式如下
第11行,三个空格分隔的整数n,m,wn,m,w
第22到 m+1m+1 行,三个空格分隔的数字 s,e,ts,e,t,表示 s,es,e 之间存在双向道路,通行需要消耗tt,允许两点间存在多条路径
第m+2m+2到m+w+1m+w+1行三个空间分隔的数字 s,e,ts,e,t,表示存在从 ss 到 ee 的单向时空隧道,穿过时空隧道时光倒流 tt

输出格式
对于每个测试用例,如果小H能回到过去则输出YES,否则输出NO
每个测试用例的输出占一行

样例输入
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
样例输出
NO
YES
数据规模和约定
1\leq n\leq 500,1\leq m\leq 4\times 10^4,1\leq w\leq 2001≤n≤500,1≤m≤4×10 4 ,1≤w≤200
1\leq T_i,T_j\leq 10^41≤T i,T j ≤10 4

#include <iostream>

#define INF 1e8

void app()
{
    int **maps;
    int n, m, w;
    scanf("%d %d %d", &n, &m, &w);
    maps = new int*[n + 1];
    for (int i = 1; i <= n; i++)
    {
        maps[i] = new int[n + 1];
        for (int j = 1; j <= n; j++)
        {
            maps[i][j] = INF;
        }
    }
    for (int i = 0; i < m; i++)
    {
        int x, y, s;
        scanf("%d %d %d", &x, &y, &s);
        maps[x][y] = s < maps[x][y] ? s : maps[x][y];
        maps[y][x] = s < maps[y][x] ? s : maps[y][x];
    }
    for (int i = 0; i < w; i++)
    {
        int x, y, s;
        scanf("%d %d %d", &x, &y, &s);
        maps[x][y] = -s < maps[x][y] ? -s : maps[x][y];
    }

    bool ok = false;
    for (int k = 1; k <= n && !ok; k++)
    {
        for (int j = 1; j <= n && !ok; j++)
        {
            for (int i = 1; i <= n && !ok; i++)
            {
                maps[i][j] = maps[i][j] > maps[i][k] + maps[k][j] ? maps[i][k] + maps[k][j] : maps[i][j];
                if (maps[i][j] + maps[j][i] < 0)
                {
                    ok = true;
                }
            }
        }
    }
    if (ok)
    {
        printf("YES\n");
    }
    else
    {
        printf("NO\n");
    }
}

int main()
{
    int c;
    scanf("%d", &c);
    while (c)
    {
        app();
        c--;
    }
    return 0;
}

在这里插入图片描述

D : 差旅花费

问题描述
有n个车站,其中1号车站为始发站,现有n-1个人,你需要安排他们分别去往除始发站以外的n-1个车站,然后返回始发站。交通系统的所有路径均为单向路径,连接两个不同的车站,每经过一条路径需要交纳一定的费用,你能求出总花费的最低金额嘛

输入格式
第一行一个整数T,表示测试用例的个数。
对于每个测试用例,输入格式如下
第一行两个整数n,m,分别表示车站的数量和车站之间的单向路径数。
接下来m行,每行三个数s,e,c,表示存在从s到e的单向路径,花费为c

输出格式
对于每个测试用例,输出其总花费的最低金额,每个测试用例的输出占一行。

样例输入
2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50
样例输出
46
210
数据规模和约定
1<=n,m<=1000000
价格c为正整数,且保证其总和小于1000000000

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

using std::pair;
using std::make_pair;
using std::priority_queue;

#define MEM 2000000
#define INF 1e9

typedef long llu;
typedef pair<llu, llu> par;

llu dis1[MEM], dis2[MEM], vstd1[MEM], vstd2[MEM], n, m, w;
llu tot1, tot2, point1[MEM], point2[MEM];
llu w1[MEM], w2[MEM], v1[MEM], v2[MEM], next1[MEM], next2[MEM];

void add(llu x, llu y, llu w)
{
    tot1++;
    next1[tot1] = point1[x];
    point1[x] = tot1;
    v1[tot1] = y;
    w1[tot1] = w;

    tot2++;
    next2[tot2] = point2[y];
    point2[y] = tot2;
    v2[tot2] = x;
    w2[tot2] = w;
}

void dijkstra(llu s, llu *dis, llu *vstd, llu *v, llu *w, llu *point, llu *next)
{
    priority_queue<par, std::vector<par>, std::greater<par> > q;
    for (llu i = 1; i <= n; i++) { dis[i] = INF; vstd[i] = 0; }
    dis[s] = 0;
    q.push(make_pair(0, s));
    while (!q.empty())
    {
        llu x = q.top().second;
        q.pop();
        if (!vstd[x])
        {
            vstd[x] = 1;
            for (llu i = point[x]; i; i = next[i])
            {
                if (dis[v[i]] > dis[x] + w[i])
                {
                    dis[v[i]] = dis[x] + w[i];
                    q.push(make_pair(dis[v[i]], v[i]));
                }
            }
        }
    }
}

void init()
{
    tot1 = 0;
    tot2 = 0;
    for (llu i = 0; i < MEM; i++)
    {
        dis1[i] = 0;
        dis2[1] = 0;
        vstd1[i] = 0;
        vstd2[i] = 0;
        point1[i] = 0;
        point2[i] = 0;
        w1[i] = 0;
        w2[i] = 0;
        v1[i] = 0;
        v2[i] = 0;
        next1[i] = 0;
        next2[i] = 0;
    }
}

void app()
{
    llu cost = 0;
    init();
    scanf("%ld %ld", &n, &m);
    for (llu i = 0; i < m; i++)
    {
        llu x, y, s;
        scanf("%ld %ld %ld", &x, &y, &s);
        add(x, y, s);
    }

    dijkstra(1, dis1, vstd1, v1, w1, point1, next1);
    dijkstra(1, dis2, vstd2, v2, w2, point2, next2);

    for (llu i = 2; i <= n; i++)
    {
        cost += dis1[i] + dis2[i];
    }
    printf("%lu\n", cost);
}

int main()
{
    int c;
    scanf("%d", &c);
    while (c)
    {
        app();
        c--;
    }
    return 0;
}

在这里插入图片描述

E : 运输货物

问题描述
考虑一个具有N个顶点,M条边的无向图。编号为1的顶点对应于一个矿山,从中提取一些珍贵的矿物。编号为N的顶点对应于一家矿物加工厂。每条边连接两个不同的顶点并拥有有两个参数,分别为最大承重量C和通行时间D。现在将从矿山中提取的矿物并选择一条路径将提取的矿物运送至工厂。该路径应具有最大的承重量,以便能够同时运输尽可能多的矿物。路径的承重量等于路径中经过的边的最大承重量的最小值。但是,这些矿物非常敏感,一旦从矿山中提取出来,它们将在T时间单位后开始分解,除非他们在此时间间隔内到达工厂。因此,所选路径的总行进时间(其路径的通行时间之和)应小于或等于T。

输入格式
输入的第一行包含一个整数X,表示测试用例的数量。
每个测试用例的第一行包含3个整数,并用空格分隔:N,M,T。接下来的M行中的每行将包含四个整数,每个数字用空格分隔:A,B,C和D,这意味着顶点A和B之间存在一条边,最大承重量为C,通行时间为D。A和B是1和N之间的不同整数。任何两个顶点之间最多存在一个边。

输出格式
对于X个测试用例,请输出在满足通行时间限制下的路径最大承重量,每个测试用例对应一行。
数据保证图中至少存在一条1到n通行总时间小于等于T的路径,即一定有解。

样例输入
2
2 1 10
1 2 13 10
4 4 20
1 2 1000 15
2 4 999 6
1 3 100 15
3 4 99 4
样例输出
13
99
数据规模和约定
1≤𝑛≤10000, 1≤m≤50000,1≤𝑇≤500000
1≤𝐶≤10000000, 1≤𝐷≤50000

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

using std::priority_queue;
using std::pair;
using std::make_pair;

#define MEM 100000
#define INF 10000000000

typedef long unsigned llu;
typedef pair<llu, llu> par;

llu dis[MEM], point[MEM], next[MEM], wl[MEM], ct[MEM], v[MEM];
llu mrkd[MEM];
llu n, m;
llu t, w;
llu tot;

void init()
{
    tot = MEM - 1;
    while (1)
    {
        dis[tot] = INF;
        point[tot] = 0;
        next[tot] = 0;
        wl[tot] = 0;
        ct[tot] = 0;
        v[tot] = 0;
        mrkd[tot] = 0;
        if (tot == 0) break;
        tot--;
    }
}

void add(llu x, llu y, llu ww, llu tt)
{
    tot++;                      // 获得新的内存地址(为新边节点申请内存)
    next[tot] = point[x];       // 新边节点的下一个指向旧边节点
    point[x] = tot;             // 点x的指针更新指向新边节点
    v[tot] = y;                 // 新边节点设置第二个连接点
    wl[tot] = ww;               // 为新边设置参数
    ct[tot] = tt;
}

void dijkstra(llu s, llu wwb)
{
    priority_queue<par, std::vector<par>, std::greater<par> > q;
    for (llu i = 1; i <= n; i++) { dis[i] = INF; mrkd[i] = 0; }
    dis[s] = 0;
    q.push(make_pair(dis[s], s));
    while (!q.empty())
    {
        llu x = q.top().second;
        q.pop();
        if (!mrkd[x])
        {
            mrkd[x] = 1;
            for (llu p = point[x]; p; p = next[p])
            {
                if (wl[p] >= wwb && dis[v[p]] > dis[x] + ct[p])
                {
                    dis[v[p]] = dis[x] + ct[p];
                    q.push(make_pair(dis[v[p]], v[p]));
                }
            }
        }
    }
}

bool inTime(llu mw)
{
    dijkstra(1, mw);
    if (dis[n] > t)
    {
        return false;
    }
    return true;
}

void app()
{
    llu ix, iy, iw, it;
    llu leftw = 0, rightw = 3000000, mid;
    init();
    scanf("%lu %lu %lu", &n, &m, &t);
    for (llu i = 0; i < m; i++)
    {
        scanf("%lu %lu %lu %lu", &ix, &iy, &iw, &it);
        leftw = leftw > iw ? iw : leftw;
        rightw = rightw < iw ? iw : rightw;
        add(ix, iy, iw, it);
        add(iy, ix, iw, it);
    }

    mid = (leftw + rightw) / 2;
    while (mid != leftw && mid != rightw)
    {
        if (inTime(mid))
        {
            leftw = mid;
        }
        else
        {
            rightw = mid;
        }
        mid = (leftw + rightw) / 2;
    }
    printf("%lu\n", inTime(rightw) ? rightw : leftw);
}

int main()
{
    int att;
    scanf("%d", &att);
    while (att)
    {
        app();
        att--;
    }
    return 0;
}



在这里插入图片描述

思路

第一题挺简单的,直接写就行:自己和相邻商店的平均值,头和尾只有自己和前后其中一个;中间的有头尾+自己一共三个。输入进来的菜价数组经过一顿操作之后再输出出来就是答案了。
第二题也可以直接写,不过比第一题复杂一些,需要搞好关系,不要把语句嵌套写乱了。最后输出累和的结果就行了。
第三题求最短路,然后比较能不能回去,这题里的时间就是权重,不过这里多了个时空倒流,就把他看成负的了:
在这里插入图片描述
就可以这样做了,在这里插入图片描述
差旅花费问题:要把所有点都去一遍,然后用最小的权重返回回来,再算上次数。实现对我来说有点难。
课上讲了个例题,有这样的思路:
在这里插入图片描述
在这里插入图片描述
上学期学的松弛操作,有的地方都记不清了,百度解释:松弛操作是指对于每个顶点v∈V,都设置一个属性d[v],用来描述从源点s到v的最短路径上权值的上界,称为最短路径估计(shortest-path estimate)。

第五题太难了,基本不会做。看看网上也就能写个迪杰特斯拉的函数。自己是写不出来了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值