POJ 1511 Invitation Cards

题目大意:

        在电视时代没有多少人会去剧院看戏,而Malidinesia剧院的老喜剧艺术家很在乎这个,他想向大众宣传他的古老的喜剧艺术。剧院印了很多邀请函(上面附上了很多必要的信息以及剧院的节目单),很多学生被雇佣去向人们分发这些邀请函(就是宣传单),这些学生被指派到指定的公交站向来来往往坐公交的乘客分发这些传单,当然,在发传单之前这些学生都做了培训,教他们如何去影响人而不是强迫人。

        城市里的交通系统比较特殊,每条公交线路都是单向的(即有向图),每条线路连接着两个公交站,没半个小时就有一辆公交车载着乘客离开始发站,当到达终点站后就会返程(回到起点),各站之间的费用记录在一张表上,费用可以当场支付,在线路中有一个CCS站,在该站上需要做例行的安全检查,包括身体扫描等。

        所有ACM成员每天早上从CCS出发,去事先指定好的站分发传单,成员数和站的总数相等,在一天结束时每个成员返回到CCS站结束一天的工作,现需要计算一天下来的最小交通费用。

        有多个测例(测例数题中给出),P个站Q条线路(每条线路都会指定起始站、目标站和费用),其中1 ≤ P, Q ≤ 1,000,000,每条线路的费用C < 1,000,000,000。

题目链接

SPFA(Shortest Path Fast Algorithm,最短路径快速算法):

注释代码:

/*                 
 * Problem ID : POJ 1511 Invitation Cards
 * Author     : Lirx.t.Una                 
 * Language   : GCC       
 * Run Time   : 1688 ms                 
 * Run Memory : 28784 KB                 
*/   

#pragma GCC optimize("O2")

#include <string.h>
#include <stdio.h>

//定义空指针
//由于这里邻接表用数组的方法实现
//因此指针类型变成了整型
#define	NIL			0

#define	TRUE		1
#define	FALSE		0

//maximum number of stops and bus lines
//公交站和公交路线的总数
//都是1,000,000
#define	MAXN		1000001
//infinity
//即总费用的极限值
//这里取long long的最大值
#define	INF			0x1000000000000000

typedef	long long	INT64;
typedef	char		BOOL;

typedef	struct {

	int		u;//起始站
	int		v;//目标站
	int		c;//cost,路程费用
} Arc;//弧,即有向边结点

//以下三个数组一起组成邻接表数据结构
Arc		arc[MAXN];//有向边
//边表头,其中head[u]就表示
//从u点出发可以直达的路线所表示的链表的表头
//在这里head[u]指向的是以u为出发点的有向边的链表表头
int		head[MAXN];
//在边链表中一条边的下一条边的指针
//next[i]表示边i的下一条边的指针
int		next[MAXN];
/*总的邻接表逻辑结构是这样的:
  u     arc
  1->   3(1, 4, 10)->7(1, 9, 30)
  2->   5(2, 8, 9)->14(2, 12, 100)
  ......
  ......
*/

//in[i]表示站i是否在栈中
BOOL	in[MAXN];

int		e;//number of edge,表示边的序号

INT64	dp[MAXN];//动态规划存放1到各点的最小费用
              //dp[i]表示1到i点的当前最小费用

int		stk[MAXN];//spfa栈
int		top;//栈顶指针

void
addarc( int u, int v, int c ) {//向邻接表中添加边

	arc[e].u = u;
	arc[e].v = v;
	arc[e].c = c;

	next[e] = head[u];
	head[u] = e++;
}

void
init( int n, int m ) {//对邻接表进行初始化

	int		u;
	int		v;
	int		c;

	memset( head + 1, NIL, n * sizeof(int));
	memset( next + 1, NIL, m * sizeof(int));

	e = 1;
	while ( m-- ) {
	
		scanf("%d%d%d", &u, &v, &c);
		addarc( u, v, c );
	}
}

void
uinit( int n, int m ) {//uninit,对图求逆图

	int		i;
	Arc		atmp;

	memset( head + 1, NIL, n * sizeof(int));
	memset( next + 1, NIL, m * sizeof(int));

	e = 1;
	for ( i = 1; i <= m; i++ ) {
	
		atmp = arc[i];
		addarc( atmp.v, atmp.u, atmp.c );
	}
}

BOOL
relax( int u, int v, int c ) {//spfa松弛

	INT64	I64tmp;

	I64tmp = dp[u] + (INT64)c;

	if ( I64tmp < dp[v] ) {
	
		dp[v] = I64tmp;
		return TRUE;
	}

	return FALSE;
}

void
spfa(int n) {//求1到个点的最短路径(即最小费用)

	int		i, j;//计数变量
	int		u, v;//起、终点

	//初始化
	dp[1] = 0;//自己到自己不用钱
	for ( i = 2; i <= n; i++ )
		dp[i] = INF;//在动态规划之前费用都设为最大
	memset(in + 1, FALSE, n * sizeof(BOOL));//都还没入站
	in[1]  = TRUE;//先将起点入站
	top	   = 1;//top=0为空
	stk[1] = 1;//将1结点入站

	//以下是spfa动态规划
	//过程类似广度优先搜索
	//条件是满足松弛条件(即有弹性的)结点(且还没入栈)入栈待下一次进行扩展
	//因为只有有弹性的
	while ( top ) {
	
		u 	  = stk[top--];//将栈顶弹栈并记录出栈
		in[u] = FALSE;//记录出栈

		for ( j = head[u]; j; j = next[j] ) {//对出栈元素进行扩展
		
			v = arc[j].v;
			//注:这里在松弛的时候是产生副作用的,即判断松弛的同时也对其进行动态规划了
			if ( relax( u, v, arc[j].c ) && !in[v] ) {//若有弹性且不在栈内
			
				//则作为优秀分子入栈等待下一步扩展
				stk[++top] = v;
				in[v]	   = TRUE;
			}
		}
	}
}

INT64
sum(int n) {//返回1到各点的dp和

	INT64	s;
	int		i;

	for ( s = 0, i = 1; i <= n; i++ )
		s += dp[i];

	return s;
}

int
main() {

	int		t;//测例数
	int		n, m;//站点和线路数
	INT64	tot;//total fee of a day,一天下来的总费用

	scanf("%d", &t);
	while ( t-- ) {
	
		scanf("%d%d", &n, &m);
		init( n, m );//顺图初始
		spfa(n);//求1到各点的最小费用
		tot = sum(n);//累加一次

		uinit( n, m );//逆图一下
		spfa(n);
		tot += sum(n);//再累加一次,求逆向费用总和

		printf("%lld\n", tot);
	}

	return 0;
}

无注释代码:

#pragma GCC optimize("O2")

#include <string.h>
#include <stdio.h>

#define	NIL			0
#define	TRUE		1
#define	FALSE		0

#define	MAXN		1000001
#define	INF			0x1000000000000000

typedef	long long	INT64;
typedef	char		BOOL;

typedef	struct {

	int		u;
	int		v;
	int		c;
} Arc;

Arc		arc[MAXN];
int		head[MAXN];
int		next[MAXN];
BOOL	in[MAXN];

int		e;

INT64	dp[MAXN];

int		stk[MAXN];
int		top;

void
addarc( int u, int v, int c ) {

	arc[e].u = u;
	arc[e].v = v;
	arc[e].c = c;

	next[e] = head[u];
	head[u] = e++;
}

void
init( int n, int m ) {

	int		u;
	int		v;
	int		c;

	memset( head + 1, NIL, n * sizeof(int));
	memset( next + 1, NIL, m * sizeof(int));

	e = 1;
	while ( m-- ) {
	
		scanf("%d%d%d", &u, &v, &c);
		addarc( u, v, c );
	}
}

void
uinit( int n, int m ) {

	int		i;
	Arc		atmp;

	memset( head + 1, NIL, n * sizeof(int));
	memset( next + 1, NIL, m * sizeof(int));

	e = 1;
	for ( i = 1; i <= m; i++ ) {
	
		atmp = arc[i];
		addarc( atmp.v, atmp.u, atmp.c );
	}
}

BOOL
relax( int u, int v, int c ) {

	INT64	I64tmp;

	I64tmp = dp[u] + (INT64)c;

	if ( I64tmp < dp[v] ) {
	
		dp[v] = I64tmp;
		return TRUE;
	}

	return FALSE;
}

void
spfa(int n) {

	int		i, j;
	int		u, v;

	dp[1] = 0;
	for ( i = 2; i <= n; i++ )
		dp[i] = INF;
	memset(in + 1, FALSE, n * sizeof(BOOL));
	in[1]  = TRUE;
	top	   = 1;
	stk[1] = 1;

	while ( top ) {
	
		u 	  = stk[top--];
		in[u] = FALSE;

		for ( j = head[u]; j; j = next[j] ) {
		
			v = arc[j].v;
			if ( relax( u, v, arc[j].c ) && !in[v] ) {
			
				stk[++top] = v;
				in[v]	   = TRUE;
			}
		}
	}
}

INT64
sum(int n) {

	INT64	s;
	int		i;

	for ( s = 0, i = 1; i <= n; i++ )
		s += dp[i];

	return s;
}

int
main() {

	int		t;
	int		n, m;
	INT64	tot;

	scanf("%d", &t);
	while ( t-- ) {
	
		scanf("%d%d", &n, &m);
		init( n, m );
		spfa(n);
		tot = sum(n);

		uinit( n, m );
		spfa(n);
		tot += sum(n);

		printf("%lld\n", tot);
	}

	return 0;
}

单词解释:

theater:n, 电影院,剧院

invitation:n, 邀请

antique:adj, 古老的,年代久远的

comedian:n, 喜剧演员

propagate:vt, 传播,宣传

most of all:最重要的是

comedy:n, 喜剧

invitation card:n, 邀请函

programme:n, 节目,节目单,程序

be hired to:被雇去干....

distribute:vt, 分配,散布

assign:vt, 指派,分派

influence:vt/n, 影响,改变

robbery:n, 抢劫

unidirectional:adj, 单向的

originating:adj, 起源的,始发的

denote:vt, 表示

fee:n, 费用(交通费用等,如公交费)

table:n, 表格

on the spot:adv, 当场,立即

payable:adj, 可支付的,应付的

round:n, 圆,循环,一回合

checkpoint:n, 检查站,关卡

scan:n, 扫描

predetermined:adj, 事先定好的

minimize:vt, 使减到最小

designate:vt, 指定,指派

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值