pdd笔试真题-和差方程最少标定(C++/Py/Java /Js/Go)

和差方程最少标定

拼多多 8月23号笔试 真题 第四题

拼多多真题目录点击查看: 拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解

题目内容

计量室要对 p p p 个样品的整数量值 x 1 , x 2 , ... , x p x_1,x_2,\ldots,x_p x1,x2,...,xp 做交叉核对。仪器打出 e e e 条记录,每条只可能是下面两种之一:

  • D u u u v v v d d d:表示差关系 x u − x v = d x_u-x_v=d xu−xv=d
  • S u u u v v v s s s:表示和关系 x u + x v = s x_u+x_v=s xu+xv=s
    量值必须是整数,可正可负可为零。请先判断这 e e e 条记录有没有一组整数解;若有,再问至少要实测多少个样品的量值,才能把全部 p p p 个量值都唯一定下来。一条记录里的两个下标可以相同。

输入描述

第一行两个整数 p p p 和 e e e( 1 ≤ p ≤ 100000 1\le p\le 100000 1≤p≤100000, 0 ≤ e ≤ 100000 0\le e\le 100000 0≤e≤100000),表示样品数与记录数。

接下来 e e e 行,每行一个大写字母 DS,随后三个整数 u , v , w u,v,w u,v,w( 1 ≤ u , v ≤ p 1\le u,v\le p 1≤u,v≤p, ∣ w ∣ ≤ 1000000000 |w|\le 1000000000 ∣w∣≤1000000000)。字母为 D 时表示 x u − x v = w x_u-x_v=w xu−xv=w,为 S 时表示 x u + x v = w x_u+x_v=w xu+xv=w。

输出描述

第一行输出 YESNO,表示是否存在整数解。

第二行:若第一行是 YES,输出最少实测个数(可以为 0,表示记录本身已经唯一确定全部量值);若第一行是 NO,输出 -1

样例1

输入

复制代码
2 2
S 1 2 8
D 1 2 2

输出

复制代码
YES
0

说明

x 1 + x 2 = 8 x_1+x_2=8 x1+x2=8 且 x 1 − x 2 = 2 x_1-x_2=2 x1−x2=2,解得 x 1 = 5 x_1=5 x1=5, x 2 = 3 x_2=3 x2=3,已经唯一,不必再测。

样例2

输入

复制代码
1 1
S 1 1 5

输出

复制代码
NO
-1

说明

2 x 1 = 5 2x_1=5 2x1=5,不是整数,无解。

样例3

输入

复制代码
3 2
D 1 2 1
D 2 3 1

输出

复制代码
YES
1

说明

只有差关系,三个量由一个自由变量决定,实测任意一个即可推出其余。

数据范围

  • 1 ≤ p ≤ 100000 1\le p\le 100000 1≤p≤100000
  • 0 ≤ e ≤ 100000 0\le e\le 100000 0≤e≤100000
  • 1 ≤ u , v ≤ p 1\le u,v\le p 1≤u,v≤p
  • ∣ w ∣ ≤ 1000000000 |w|\le 1000000000 ∣w∣≤1000000000
  • 线索类型仅为 DS
  • 所有编号与关系值均为整数

题解

思路

解题思路: 并查集

  1. 并查集内不关需要维护属于哪个集合,元素还需要维护x_u = sign_u * x_r + offset_u和连通块根的关系。x_r为连通块根。
  2. 正常情况下需要实测个数 == 连通块数量,但存在以下两种例如情况:
    • 无解情况,发生在同一连通块中,前后关系矛盾例如
      • 存在x_u​=s_u * ​x_r​+d_u​x_v​=s_v​ * x_r ​+d_v​
      • 此时引入新的约束x_u = k * x_v + w
      • 带入得到新的关系(s_u​− k* s_v​)x_r​=kd_v​+w−d_u​
      • s_u ∈ {-1,1},s_v ∈ {-1,1},k ∈ {-1,1}
      • 左边系数只会存在两种情况
        • 系数为0,那么kd_v​+w−d_u == 0不成立则说明矛盾
        • 系数为±2, 需要满足(k * d_v + w - d_u) % 2 == 0,不成立则矛盾
    • 连通块内只要有一个值被确定,则不需要进行测量。其实也就是上述无解情境下,引入新的约束之后,不发生冲突,那么就能确认一个值。x1 = x3 + 2, x2 = x3 - 2, 如果此时引入x1 = kx2 + w不发生冲突下,带入就能确认连通块中任意一个值。
  3. 在有解情况下,题目的答案就是不能确认值的连通块数量。上述就是这个题目的总体思路,具体实现逻辑可参照下面代码。
  4. 代码时间复杂度为O(p+e)

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

using ll = long long;

struct DSU {
    vector<int> parent, sign;
    vector<ll> offset;
    vector<bool> fixed;

    DSU(int n) {
        parent.resize(n + 1);
        sign.resize(n + 1, 1);
        offset.resize(n + 1, 0);
        fixed.resize(n + 1, false);

        for (int i = 1; i <= n; i++) {
            parent[i] = i;
        }
    }

    // x_u = sign[u] * x_parent[u] + offset[u]
    int find(int u) {
        if (parent[u] == u) {
            return u;
        }

        int p = parent[u];
        int r = find(p);

        // x_p = sign[p] * x_r + offset[p]
        // x_u = sign[u] * x_p + offset[u]
        offset[u] += (ll)sign[u] * offset[p];
        sign[u] *= sign[p];

        parent[u] = r;
        return r;
    }

    // 添加关系:x_u = k * x_v + w
    bool unite(int u, int v, int k, ll w) {
        int ru = find(u);
        int rv = find(v);

        int su = sign[u];
        ll du = offset[u];

        int sv = sign[v];
        ll dv = offset[v];

        // 已经在同一个连通块
        if (ru == rv) {
            // (su - k * sv) * x_root
            // = k * dv + w - du
            int coef = su - k * sv;
            ll rhs = k * dv + w - du;

            if (coef == 0) {
                // 0 = rhs
                return rhs == 0;
            }

            // coef 一定是 2 或 -2
            // 要求 x_root 为整数
            if (rhs % 2 != 0) {
                return false;
            }

            // 自由变量被唯一确定
            fixed[ru] = true;
            return true;
        }

        // x_ru = A * x_rv + B
        int A = su * k * sv;
        ll B = (ll)su * (k * dv + w - du);

        parent[ru] = rv;
        sign[ru] = A;
        offset[ru] = B;

        // 如果 ru 原本已经被固定,
        // 合并后 rv 所在的连通块也被固定
        fixed[rv] = fixed[rv] || fixed[ru];

        return true;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int p, e;
    cin >> p >> e;

    DSU dsu(p);

    bool ok = true;

    for (int i = 0; i < e; i++) {
        char type;
        int u, v;
        ll w;

        cin >> type >> u >> v >> w;

        // D:x_u - x_v = w
        // S:x_u + x_v = w
        int k = (type == 'D' ? 1 : -1);

        if (ok && !dsu.unite(u, v, k, w)) {
            ok = false;
        }
    }

    if (!ok) {
        cout << "NO\n";
        cout << "-1\n";
        return 0;
    }

    // 没有被方程唯一确定的连通块,每个至少需要实测一个样品
    int ans = 0;

    for (int i = 1; i <= p; i++) {
        if (dsu.find(i) == i && !dsu.fixed[i]) {
            ans++;
        }
    }

    cout << "YES\n";
    cout << ans << '\n';

    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    static class DSU {
        int[] parent, sign;
        long[] offset;
        boolean[] fixed;

        DSU(int n) {
            parent = new int[n + 1];
            sign = new int[n + 1];
            offset = new long[n + 1];
            fixed = new boolean[n + 1];

            Arrays.fill(sign, 1);
            for (int i = 1; i <= n; i++) {
                parent[i] = i;
            }
        }

        // x_u = sign[u] * x_parent[u] + offset[u]
        int find(int u) {
            if (parent[u] == u) {
                return u;
            }

            int p = parent[u];
            int r = find(p);

            // x_p = sign[p] * x_r + offset[p]
            // x_u = sign[u] * x_p + offset[u]
            offset[u] += (long) sign[u] * offset[p];
            sign[u] *= sign[p];

            parent[u] = r;
            return r;
        }

        // 添加关系:x_u = k * x_v + w
        boolean unite(int u, int v, int k, long w) {
            int ru = find(u);
            int rv = find(v);

            int su = sign[u];
            long du = offset[u];

            int sv = sign[v];
            long dv = offset[v];

            // 已经在同一个连通块
            if (ru == rv) {
                // (su - k * sv) * x_root
                // = k * dv + w - du
                int coef = su - k * sv;
                long rhs = k * dv + w - du;

                if (coef == 0) {
                    // 0 = rhs
                    return rhs == 0;
                }

                // coef 一定是 2 或 -2
                // 要求 x_root 为整数
                if (rhs % 2 != 0) {
                    return false;
                }

                // 自由变量被唯一确定
                fixed[ru] = true;
                return true;
            }

            // x_ru = A * x_rv + B
            int A = su * k * sv;
            long B = (long) su * (k * dv + w - du);

            parent[ru] = rv;
            sign[ru] = A;
            offset[ru] = B;

            // 如果 ru 原本已经被固定,
            // 合并后 rv 所在的连通块也被固定
            fixed[rv] = fixed[rv] || fixed[ru];

            return true;
        }
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine());
        int p = Integer.parseInt(st.nextToken());
        int e = Integer.parseInt(st.nextToken());

        DSU dsu = new DSU(p);

        boolean ok = true;

        for (int i = 0; i < e; i++) {
            st = new StringTokenizer(br.readLine());

            char type = st.nextToken().charAt(0);
            int u = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
            long w = Long.parseLong(st.nextToken());

            // D:x_u - x_v = w
            // S:x_u + x_v = w
            int k = (type == 'D' ? 1 : -1);

            if (ok && !dsu.unite(u, v, k, w)) {
                ok = false;
            }
        }

        if (!ok) {
            System.out.println("NO");
            System.out.println("-1");
            return;
        }

        // 没有被方程唯一确定的连通块,每个至少需要实测一个样品
        int ans = 0;

        for (int i = 1; i <= p; i++) {
            if (dsu.find(i) == i && !dsu.fixed[i]) {
                ans++;
            }
        }

        System.out.println("YES");
        System.out.println(ans);
    }
}

python

python 复制代码
import sys

class DSU:
    def __init__(self, n):
        self.parent = list(range(n + 1))
        self.sign = [1] * (n + 1)
        self.offset = [0] * (n + 1)
        self.fixed = [False] * (n + 1)

    # x_u = sign[u] * x_parent[u] + offset[u]
    def find(self, u):
        if self.parent[u] == u:
            return u

        p = self.parent[u]
        r = self.find(p)

        # x_p = sign[p] * x_r + offset[p]
        # x_u = sign[u] * x_p + offset[u]
        self.offset[u] += self.sign[u] * self.offset[p]
        self.sign[u] *= self.sign[p]

        self.parent[u] = r
        return r

    # 添加关系:x_u = k * x_v + w
    def unite(self, u, v, k, w):
        ru = self.find(u)
        rv = self.find(v)

        su = self.sign[u]
        du = self.offset[u]

        sv = self.sign[v]
        dv = self.offset[v]

        # 已经在同一个连通块
        if ru == rv:
            # (su - k * sv) * x_root
            # = k * dv + w - du
            coef = su - k * sv
            rhs = k * dv + w - du

            if coef == 0:
                # 0 = rhs
                return rhs == 0

            # coef 一定是 2 或 -2
            # 要求 x_root 为整数
            if rhs % 2 != 0:
                return False

            # 自由变量被唯一确定
            self.fixed[ru] = True
            return True

        # x_ru = A * x_rv + B
        A = su * k * sv
        B = su * (k * dv + w - du)

        self.parent[ru] = rv
        self.sign[ru] = A
        self.offset[ru] = B

        # 如果 ru 原本已经被固定,
        # 合并后 rv 所在的连通块也被固定
        self.fixed[rv] = self.fixed[rv] or self.fixed[ru]

        return True


def main():
    input = sys.stdin.readline

    p, e = map(int, input().split())

    dsu = DSU(p)

    ok = True

    for _ in range(e):
        type_, u, v, w = input().split()
        u = int(u)
        v = int(v)
        w = int(w)

        # D:x_u - x_v = w
        # S:x_u + x_v = w
        k = 1 if type_ == 'D' else -1

        if ok and not dsu.unite(u, v, k, w):
            ok = False

    if not ok:
        print("NO")
        print("-1")
        return

    # 没有被方程唯一确定的连通块,每个至少需要实测一个样品
    ans = 0

    for i in range(1, p + 1):
        if dsu.find(i) == i and not dsu.fixed[i]:
            ans += 1

    print("YES")
    print(ans)


if __name__ == "__main__":
    main()

javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let lines = [];
rl.on('line', line => {
    lines.push(line);
});

rl.on('close', () => {
    let idx = 0;

    class DSU {
        constructor(n) {
            this.parent = new Int32Array(n + 1);
            this.sign = new Int32Array(n + 1);
            this.offset = new Float64Array(n + 1);
            this.fixed = new Array(n + 1).fill(false);

            for (let i = 1; i <= n; i++) {
                this.parent[i] = i;
                this.sign[i] = 1;
            }
        }

        // x_u = sign[u] * x_parent[u] + offset[u]
        find(u) {
            if (this.parent[u] === u) {
                return u;
            }

            const p = this.parent[u];
            const r = this.find(p);

            // x_p = sign[p] * x_r + offset[p]
            // x_u = sign[u] * x_p + offset[u]
            this.offset[u] += this.sign[u] * this.offset[p];
            this.sign[u] *= this.sign[p];

            this.parent[u] = r;
            return r;
        }

        // 添加关系:x_u = k * x_v + w
        unite(u, v, k, w) {
            const ru = this.find(u);
            const rv = this.find(v);

            const su = this.sign[u];
            const du = this.offset[u];

            const sv = this.sign[v];
            const dv = this.offset[v];

            // 已经在同一个连通块
            if (ru === rv) {
                // (su - k * sv) * x_root
                // = k * dv + w - du
                const coef = su - k * sv;
                const rhs = k * dv + w - du;

                if (coef === 0) {
                    // 0 = rhs
                    return rhs === 0;
                }

                // coef 一定是 2 或 -2
                // 要求 x_root 为整数
                if (rhs % 2 !== 0) {
                    return false;
                }

                // 自由变量被唯一确定
                this.fixed[ru] = true;
                return true;
            }

            // x_ru = A * x_rv + B
            const A = su * k * sv;
            const B = su * (k * dv + w - du);

            this.parent[ru] = rv;
            this.sign[ru] = A;
            this.offset[ru] = B;

            // 如果 ru 原本已经被固定,
            // 合并后 rv 所在的连通块也被固定
            this.fixed[rv] = this.fixed[rv] || this.fixed[ru];

            return true;
        }
    }

    const [p, e] = lines[idx++].trim().split(/\s+/).map(Number);

    const dsu = new DSU(p);

    let ok = true;

    for (let i = 0; i < e; i++) {
        const [type, uStr, vStr, wStr] = lines[idx++].trim().split(/\s+/);

        const u = Number(uStr);
        const v = Number(vStr);
        const w = Number(wStr);

        // D:x_u - x_v = w
        // S:x_u + x_v = w
        const k = type === 'D' ? 1 : -1;

        if (ok && !dsu.unite(u, v, k, w)) {
            ok = false;
        }
    }

    if (!ok) {
        console.log("NO");
        console.log("-1");
        return;
    }

    // 没有被方程唯一确定的连通块,每个至少需要实测一个样品
    let ans = 0;

    for (let i = 1; i <= p; i++) {
        if (dsu.find(i) === i && !dsu.fixed[i]) {
            ans++;
        }
    }

    console.log("YES");
    console.log(ans);
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
)

type DSU struct {
	parent []int
	sign   []int
	offset []int64
	fixed  []bool
}

func NewDSU(n int) *DSU {
	dsu := &DSU{
		parent: make([]int, n+1),
		sign:   make([]int, n+1),
		offset: make([]int64, n+1),
		fixed:  make([]bool, n+1),
	}

	for i := 1; i <= n; i++ {
		dsu.parent[i] = i
		dsu.sign[i] = 1
	}

	return dsu
}

// x_u = sign[u] * x_parent[u] + offset[u]
func (d *DSU) find(u int) int {
	if d.parent[u] == u {
		return u
	}

	p := d.parent[u]
	r := d.find(p)

	// x_p = sign[p] * x_r + offset[p]
	// x_u = sign[u] * x_p + offset[u]
	d.offset[u] += int64(d.sign[u]) * d.offset[p]
	d.sign[u] *= d.sign[p]

	d.parent[u] = r
	return r
}

// 添加关系:x_u = k * x_v + w
func (d *DSU) unite(u, v, k int, w int64) bool {
	ru := d.find(u)
	rv := d.find(v)

	su := d.sign[u]
	du := d.offset[u]

	sv := d.sign[v]
	dv := d.offset[v]

	// 已经在同一个连通块
	if ru == rv {
		// (su - k * sv) * x_root
		// = k * dv + w - du
		coef := su - k*sv
		rhs := int64(k)*dv + w - du

		if coef == 0 {
			// 0 = rhs
			return rhs == 0
		}

		// coef 一定是 2 或 -2
		// 要求 x_root 为整数
		if rhs%2 != 0 {
			return false
		}

		// 自由变量被唯一确定
		d.fixed[ru] = true
		return true
	}

	// x_ru = A * x_rv + B
	A := su * k * sv
	B := int64(su) * (int64(k)*dv + w - du)

	d.parent[ru] = rv
	d.sign[ru] = A
	d.offset[ru] = B

	// 如果 ru 原本已经被固定,
	// 合并后 rv 所在的连通块也被固定
	d.fixed[rv] = d.fixed[rv] || d.fixed[ru]

	return true
}

func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var p, e int
	fmt.Fscan(in, &p, &e)

	dsu := NewDSU(p)

	ok := true

	for i := 0; i < e; i++ {
		var typ string
		var u, v int
		var w int64

		fmt.Fscan(in, &typ, &u, &v, &w)

		// D:x_u - x_v = w
		// S:x_u + x_v = w
		k := 1
		if typ == "S" {
			k = -1
		}

		if ok && !dsu.unite(u, v, k, w) {
			ok = false
		}
	}

	if !ok {
		fmt.Fprintln(out, "NO")
		fmt.Fprintln(out, "-1")
		return
	}

	// 没有被方程唯一确定的连通块,每个至少需要实测一个样品
	ans := 0

	for i := 1; i <= p; i++ {
		if dsu.find(i) == i && !dsu.fixed[i] {
			ans++
		}
	}

	fmt.Fprintln(out, "YES")
	fmt.Fprintln(out, ans)
}
相关推荐
无限码力12 小时前
拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解
算法·pdd笔试真题·pdd笔试真题题解·拼多多春招笔试真题·拼多多秋招笔试真题·拼多多笔试真题题解
无限码力1 天前
pdd笔试真题-驿站补给最短耗时(C++/Py/Java /Js/Go)
pdd·拼多多笔试真题·拼多多机试·拼多多技术岗笔试·pdd笔试真题·拼多多技术岗笔试真题·pdd笔试真题题解
无限码力24 天前
拼多多笔试真题-评价展示序列(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多机试·拼多多技术岗笔试·拼多多笔试·拼多多技术岗笔试真题
无限码力24 天前
拼多多笔试真题-多多送快递(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多机试·拼多多笔试·拼多多技术岗真题
无限码力25 天前
拼多多笔试真题-多多的告警网络(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多笔试·拼多多技术岗笔试真题
无限码力1 个月前
拼多多笔试真题-多多的GPU批处理调度(C++/Py/Java /Js/Go)
拼多多·pdd·拼多多笔试真题·拼多多机试·拼多多笔试·拼多多技术岗笔试真题
无限码力1 个月前
拼多多笔试真题-多多的灰度发布(C++/Py/Java /Js/Go)
拼多多·拼多多机试·拼多多技术岗笔试·拼多多笔试·拼多多技术岗笔试真题
无限码力2 个月前
拼多多笔试真题【多多的特殊三元组】
拼多多·拼多多笔试真题·拼多多笔试题库·拼多多技术岗笔试·pdd笔试笔试真题
无限码力2 个月前
拼多多笔试真题-多多的Boss挑战(C++/Py/Java /Js/Go)
拼多多·拼多多笔试真题·拼多多技术岗笔试题目·拼多多机试·pdd笔试真题