A
题意
N个人,M个胜负关系表示为(A,B),胜负关系具有传递性。求出每组无法判断胜负关系的组数(A,B与B,A等价)。
- 输入
3
3 3
1 2
1 3
2 3
3 2
1 2
2 3
4 2
1 2
3 4
- 输出
0
0
4
思路
- 题目类型定位:图,找路径。
- 运用的算法:Floyd算法的模板
- 思路:Floyd算法求最短路的部分改为,通过k,ij是否能判断,或者ij直接能判断,代码描述为:
dis[i][j]=max(dis[i][j],dis[i][k]&dis[k][j]);
总结
- 直接Floyd会TLE,需要剪枝预判:
若dis[i][k]==0,则dis[i][k]&dis[k][j]一定为0,不需要再次循环判断 - Floyd模板:
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=505;
int dis[MAXN][MAXN];
int N,M;
int main(void){
int group;
scanf("%d",&group);
while(group--){
scanf("%d%d",&N,&M);
for(int x=1;x<=N;x++)
for(int y=1;y<=N;y++)
dis[x][y]=0;
for(int j=0;j<M;j++){
int A,B;
scanf("%d%d",&A,&B);
dis[A][B]=1;
}
for(int k=1;k<=N;k++){
for(int i=1;i<=N;i++){
if(dis[i][k]==0) continue;
for(int j=1;j<=N;j++){
dis[i][j]=max(dis[i][j],dis[i][k]&dis[k][j]);
}
}
}
int res=0;
for(int i=1;i<=N;i++){
for(int j=i+1;j<=N;j++){
if(dis[i][j]==0&&dis[j][i]==0)
res++;
}
}
printf("%d\n",res);
}
return 0;
}
B
题意
现可乘坐经济线和商业线,票价速度均不同。现可以乘坐1次商业线,忽略换乘时间,求到机场最快的线路。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
- 输入
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
- 输出
1 2 4
2
5
思路
- 问题定位:图,最短路问题
- 问题算法:无负边的单源最短路 ——dijkstra
- 思路:
-1. 存图:前向星,2个点存2条边(无向图)
-2. 分别从起点、终点运用dijkstra,并记录到各点的最短路结果,并设置当前最短路为不经商业线到达终点
-3. 枚举每一条商业线。将到达商业线两端的点,利用上述最短路结果连接商业线,求出与当前最短路的最小值,更新最短路结果
总结
dijkstra模板(前向星描述图):
代码
#include<cstdio>
#include<queue>
using namespace std;
const int MAXN=1e5;
const int INF=1e9;
int cases;
int N,S,E,M,K;
struct Edge{
int to,w,next;
}edges_ec[2*MAXN];
int head_ec[MAXN],tot_ec;
int dis1[MAXN],dis2[MAXN],vis[MAXN];
int pre1[MAXN],pre2[MAXN];
void init_all(){
for(int i=0;i<=N;i++)
head_ec[i]=-1;
tot_ec=0;
}
void init_ec(int from,int to,int w){
edges_ec[++tot_ec].to=to;
edges_ec[tot_ec].w=w;
edges_ec[tot_ec].next=head_ec[from];
head_ec[from]=tot_ec;
}
void display(int *pre, int i){
if(i==S){
printf("%d",i);
return;
}
display(pre,pre[i]);
printf(" %d",i);
}
void display2(int i){
if(i==E){
printf(" %d",i);
return;
}
printf(" %d",i);
display2(pre2[i]);
}
void dijkstra(int s,int *dis,int *pre){
priority_queue<pair<int,int> > q;
for(int i=0;i<=N;i++){
vis[i]=0;
dis[i]=INF;
pre[i]=0;
}
dis[s]=0;
q.push(make_pair(0,s));
while(!q.empty()){
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head_ec[x]; i!=-1; i=edges_ec[i].next){
int y=edges_ec[i].to,w=edges_ec[i].w;
if(dis[y]>dis[x]+w){
dis[y]=dis[x]+w;
pre[y]=x;
q.push(make_pair(-dis[y],y));
}
}
}
}
int main(void){
while(~scanf("%d%d%d",&N,&S,&E)){
init_all();
scanf("%d",&M);
for(int i=0;i<M;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
init_ec(x,y,z);
init_ec(y,x,z);
}
dijkstra(S,dis1,pre1);
dijkstra(E,dis2,pre2);
int uBU=0,vBU=0;
int minLength=dis1[E];
int flag=0;
scanf("%d",&K);
for(int i=0;i<K;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(dis1[v]+dis2[u]+w<minLength){
minLength=dis1[v]+dis2[u]+w;
uBU=v,vBU=u;
flag=1;
}
if(dis1[u]+dis2[v]+w<minLength){
minLength=dis1[u]+dis2[v]+w;
uBU=u,vBU=v;
flag=1;
}
}
if(cases!=0) printf("\n");
cases++;
if(flag==1){
display(pre1,uBU);
display2(vBU);
printf("\n%d\n%d\n",uBU,minLength);
}
else{
display(pre1,E);
printf("\nTicket Not Used\n%d\n",minLength);
}
}
return 0;
}
C
题意
有 N 个商业城市,编号 1 ~ N,其中 1 号城市是首都。现共有 M 条有向道路供商业城市相互往来。
每一个商业城市有正整数表示的繁荣程度,当沿道路从一个商业城市走到另一个商业城市时,都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。
现想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请打出 ‘?’。
- 输入
2
5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5
10
1 2 4 4 5 6 7 8 9 10
10
1 2
2 3
3 1
1 4
4 5
5 6
6 7
7 8
8 9
9 10
2
3 10
- 输出
Case 1:
3
4
Case 2:
?
?
思路
- 题目定位:图,求最短路
- 算法:可能有负环 ——SPFA
- 思路:运用spfa求最短路,找到负环时,用dfs将负环上的点可达的所有点标记为-INF
总结
spfa模板(图的邻接表描述):
代码
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
const int MAXN=1e5+5;
const int INF=1e9;
int N,M,a[MAXN];
struct Edge{
int to,w,next;
} edges[MAXN];
int head[MAXN],tot;
void init(){
for(int i=0;i<=N;i++) head[i]=-1;
tot=0;
}
void addEdge(int from,int to,int w){
edges[++tot].to=to;
edges[tot].w=w;
edges[tot].next=head[from];
head[from]=tot;
}
int vis[MAXN],dis[MAXN],cnt[MAXN];
int vis_dfs[MAXN];
void dfs(int p){
for(int i=head[p]; i; i=edges[i].next){
int v=edges[i].to;
if(!vis_dfs[v]){
vis_dfs[v]=1;
dis[v]=-INF;
dfs(v);
}
}
}
void spfa(int s){
queue<int> q;
for(int i=0;i<=N;i++){
vis[i]=0;
dis[i]=INF;
cnt[i]=0;
}
vis[s]=1,dis[s]=0;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u]; i; i=edges[i].next){
int v=edges[i].to,w=edges[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>=N){
dis[v]=-INF;
dfs(v);
}
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
int main(void){
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++){
init();
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&a[i]);
scanf("%d",&M);
for(int i=0;i<M;i++){
int A,B;
scanf("%d%d",&A,&B);
addEdge(A,B,pow(a[B]-a[A],3));
}
spfa(1);
printf("Case %d:\n",t);
int Q;
scanf("%d",&Q);
while(Q--){
int P;
scanf("%d",&P);
if(dis[P]<3||dis[P]==INF||dis[P]==-INF)
printf("?\n");
else
printf("%d\n",dis[P]);
}
}
return 0;
}
疑惑:前向星的遍历判断,为什么此处用 i,而不是 i != -1(i != -1 死循环。上题dijkstra用 i 遇到多余的0)
注:
人间迷惑大赏之要输出 Case i: …
结论:测试样例果然是用来测试输入输出格式的…