题目大意:
在电视时代没有多少人会去剧院看戏,而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, 指定,指派