[2021.8集训Day7 / P7297 USACO21JAN] Telephone G

[2021.8集训Day7 / P7297 USACO21JAN] Telephone G

题目

题目描述

Farmer John 的 N 头奶牛,编号为 \(1…N\),站成一行(\(1≤N≤5⋅10^4\))。第 \(i\) 头奶牛的品种编号为 \(b_i\),范围为 \(1…K\),其中 \(1≤K≤50\)。奶牛们需要你帮助求出如何最优地从奶牛 \(1\) 传输一条信息到奶牛 \(N\)

从奶牛 \(i\) 传输信息到奶牛 \(j\) 花费时间 \(|i−j|\)。然而,不是所有品种的奶牛都愿意互相交谈,如一个 \(K×K\) 的方阵 \(S\) 所表示,其中如果一头品种 \(i\) 的奶牛愿意传输一条信息给一头品种 \(j\) 的奶牛,那么 \(S_{ij}=1\),否则为 \(0\)。不保证 \(S_{ij}=S_{ji}\),并且如果品种 \(i\) 的奶牛之间不愿意互相交流时可以有 \(S_{ii}=0\)

请求出传输信息所需的最小时间。

输入格式

输入的第一行包含 \(N\)\(K\)

下一行包含 \(N\) 个空格分隔的整数 \(b_1,b_2,…,b_N\)

以下 \(K\) 行描述了方阵 \(S\)。每行包含一个由 \(K\) 个二进制位组成的字符串,从上往下第 \(i\) 个字符串的第 \(j\) 位为 \(S_{ij}\)

输出格式

输出一个整数,为需要的最小时间。如果不可能从奶牛 \(1\) 传输信息至奶牛 \(N\),输出 \(−1\)

输入输出样例

输入 #1

5 4
1 4 2 3 4
1010
0001
0110
0100

输出 #1

6

说明/提示

最优传输序列为 \(1→4→3→5\)。总时间为 \(|1−4|+|4−3|+|3−5|=6\)

测试点性质:

  • 测试点 1-5 满足 \(N≤1000\)
  • 测试点 6-13 没有额外限制。

供题:Dhruv Rohatgi

思路

建图(分层图),最短路.

对于\(k\)个颜色,开\(k\)层,每层有\(n\)个点.

对于每个颜色层,层内相邻点连一条权值为1的无向边.

对于每一只奶牛\(i\)​,设它的种类为\(x\)​,\(y\)​是另一个种类.

  • 连一条有向边: 奶牛$i\to \(第\)x\(层第\)i$个点, 边权为0
  • \(s_{y,x}=1\)连一条有向边: 第\(y\)层第\(i\)​个点\(\to\)奶牛\(i\), 边权为0

总点数\(n+n\cdot k\),总边数(一条有向边按两条无向边算)不超过\(n\cdot k+2n\cdot k\).

求奶牛\(1\)到奶牛\(n\)​的最短路即可.

最短路貌似可以双端队列实现\(O(n\cdot k)\),但是本人又懒又菜就写了SPFA

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}
const int N = 50010 , K = 100;

struct EDGE {
	int to , nxt , val;
}ed[(N * K + N) * 2];
int head[N + N * K];
void addedge(int u , int v , int val) {
	static int cnt = 0;
	++cnt;
	ed[cnt].to = v , ed[cnt].val = val , ed[cnt].nxt = head[u] , head[u] = cnt;
}

int n , k;
int col[N];
int s[K][K];

queue <int> q;
bool inq[N + N * K];

int cowid[N] , colid[K][N];//cow_id , color_id
int dist[N * K + N];
int newid() {
	static int cnt = 0;
	return ++cnt;
}
int main() {
	freopen("data//P7297_6.in" , "r" , stdin);
	n = read() , k = read();
	for(int i = 1 ; i <= n ; i++)
		col[i] = read();
	for(int i = 1 ; i <= k ; i++)
		for(int j = 1 ; j <= k ; j++) {
			char c = getchar();
			while(c != '0' && c != '1')	c = getchar();
			s[i][j] = c - '0';
		}
	
	//Get ID
	for(int i = 1 ; i <= n ; i++)
		cowid[i] = newid();
	for(int i = 1 ; i <= k ; i++)
		for(int j = 1 ; j <= n ; j++)
			colid[i][j] = newid();
	//build
	for(int i = 1 ; i <= k ; i++)
		for(int j = 1 ; j < n ; j++)
			addedge(colid[i][j] , colid[i][j + 1] , 1) , addedge(colid[i][j + 1] , colid[i][j] , 1);
	for(int i = 1 ; i <= n ; i++)
		addedge(cowid[i] , colid[col[i]][i] , 0);
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= k ; j++)
			if(s[j][col[i]])
				addedge(colid[j][i] , cowid[i] , 0);
	
	//SPFA
	memset(dist , 0x3f , sizeof(dist));
	dist[cowid[1]] = 0;
	q.push(cowid[1]) , inq[cowid[1]] = true;
	while(!q.empty()) {
		int u = q.front();
		q.pop() , inq[u] = false;
		for(int i = head[u] ; i ; i = ed[i].nxt) {
			int v = ed[i].to;
			if(dist[v] > dist[u] + ed[i].val) {
				dist[v] = dist[u] + ed[i].val;
				if(!inq[v])
					q.push(v) , inq[v] = true;
			}
		}
	}
	printf("%d" , dist[cowid[n]] == 1061109567 ? -1 : dist[cowid[n]]);
	return 0;
}
posted @ 2021-08-17 21:31  追梦人1024  阅读(48)  评论(0编辑  收藏  举报