小陈的开学第七周程序

这篇博客记录了作者在一周内的编程竞赛经历,包括在Atcoder、PTA、Vjudge和Wlacm平台上的题目。涉及到链表的操作如查找、插入、多项式运算,以及最短路径问题的解决,如Floyd算法、Bellman-Ford算法、SPFA和Dijkstra算法的应用。博客提供了解题思路和部分程序代码。

一、Atcoder

1.Walk on Multiplication Table

题意

Takahashi is standing on a multiplication table with infinitely many rows and columns.

The square (i,j) contains the integer i×j. Initially, Takahashi is standing at (1,1).

In one move, he can move from (i,j) to either (i+1,j) or (i,j+1).

Given an integer N, find the minimum number of moves needed to reach a square that contains N.

2≤N≤10^12
N is an integer.

输入

Input is given from Standard Input in the following format:N

输出

Print the minimum number of moves needed to reach a square that contains the integer N.

样例输入

10

样例输出

5

(2,5) can be reached in five moves. We cannot reach a square that contains 10 in less than five moves.

解题思路

就是问你几步能走到对应的位置,刚开始我看数据有点大,所以就不太敢写,后来发现只要开根号求解就可以了。

程序代码

#include<bits/stdc++.h>
using namespace std;
long long n,m,t;
int main(){
	scanf("%lld",&n);
	m=sqrt(n);
	if(m*m==n){
		cout<<2*m-2<<endl;
		return 0;
	}else{
		for(int i=m;i>=1;i--){
			t=n/i;
			if(t*i==n){
				cout<<i+t-2<<endl;
				break;
			}
		}
	}
	return 0;
}

二、PTA

1.链式表的按序号查找

题意

本题要求实现一个函数,找到并返回链式表的第K个元素。

L是给定单链表,函数FindKth要返回链式表的第K个元素。如果该元素不存在,则返回ERROR

裁判测试程序样例

#include <stdio.h>
#include <stdlib.h>

#define ERROR -1
typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Next;
};
typedef PtrToLNode List;

List Read(); /* 细节在此不表 */

ElementType FindKth( List L, int K );

int main()
{
    int N, K;
    ElementType X;
    List L = Read();
    scanf("%d", &N);
    while ( N-- ) {
        scanf("%d", &K);
        X = FindKth(L, K);
        if ( X!= ERROR )
            printf("%d ", X);
        else
            printf("NA ");
    }
    return 0;
}

/* 你的代码将被嵌在这里 */

样例输入

1 3 4 5 2 -1
6
3 6 1 5 4 2

样例输出

4 NA 1 2 5 3 

程序代码

ElementType FindKth(List L,int K){
	List p=L;
	while(--K){
		if(!p)
			return ERROR;
		p=p->Next;
	}
	if(p)
		return p->Data;
	return ERROR;
}

2.带头结点的链式表操作集

题意

本题要求实现带头结点的链式表操作集。

各个操作函数的定义为:

List MakeEmpty():创建并返回一个空的线性表;

Position Find( List L, ElementType X ):返回线性表中X的位置。若找不到则返回ERROR;

bool Insert( List L, ElementType X, Position P ):将X插入在位置P指向的结点之前,返回true。如果参数P指向非法位置,则打印“Wrong Position for Insertion”,返回false;

bool Delete( List L, Position P ):将位置P的元素删除并返回true。若参数P指向非法位置,则打印“Wrong Position for Deletion”并返回false。

裁判测试程序样例

#include <stdio.h>
#include <stdlib.h>

#define ERROR NULL
typedef enum {false, true} bool;
typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;

List MakeEmpty(); 
Position Find( List L, ElementType X );
bool Insert( List L, ElementType X, Position P );
bool Delete( List L, Position P );

int main()
{
    List L;
    ElementType X;
    Position P;
    int N;
    bool flag;

    L = MakeEmpty();
    scanf("%d", &N);
    while ( N-- ) {
        scanf("%d", &X);
        flag = Insert(L, X, L->Next);
        if ( flag==false ) printf("Wrong Answer\n");
    }
    scanf("%d", &N);
    while ( N-- ) {
        scanf("%d", &X);
        P = Find(L, X);
        if ( P == ERROR )
            printf("Finding Error: %d is not in.\n", X);
        else {
            flag = Delete(L, P);
            printf("%d is found and deleted.\n", X);
            if ( flag==false )
                printf("Wrong Answer.\n");
        }
    }
    flag = Insert(L, X, NULL);
    if ( flag==false ) printf("Wrong Answer\n");
    else
        printf("%d is inserted as the last element.\n", X);
    P = (Position)malloc(sizeof(struct LNode));
    flag = Insert(L, X, P);
    if ( flag==true ) printf("Wrong Answer\n");
    flag = Delete(L, P);
    if ( flag==true ) printf("Wrong Answer\n");
    for ( P=L->Next; P; P = P->Next ) printf("%d ", P->Data);
    return 0;
}
/* 你的代码将被嵌在这里 */

样例输入

6
12 2 4 87 10 2
4
2 12 87 5

样例输出

2 is found and deleted.
12 is found and deleted.
87 is found and deleted.
Finding Error: 5 is not in.
5 is inserted as the last element.
Wrong Position for Insertion
Wrong Position for Deletion
10 4 2 5 

程序代码

List MakeEmpty(){
	List L;
	L = (Position)malloc(sizeof(struct LNode));
	L->Next=NULL;
	return L;
}
Position Find( List L, ElementType X ){
	if(L->Next==NULL)
		return ERROR;
	List p=L->Next;
	while(p){
		if(p->Data==X){
			return p;
		}
		if(p->Next==NULL){
			return ERROR;
		}
		p=p->Next;
	}
//	return ERROR;
}
bool Insert(List L,ElementType X,Position P ){
	if(L->Next==NULL){
		List s;
		s=(Position)malloc(sizeof(struct LNode));
		s->Data=X;
		s->Next=NULL;
		L->Next=s;
		return true;
	}
	List p=L->Next;
	List p1=L;
	while(p){
		if(p==P){
			List s;
			s=(Position)malloc(sizeof(struct LNode));
			s->Data=X;
			s->Next=p;
			p1->Next=s;
			return true;
		}
		if(p->Next==NULL){
			if(p->Next==P){
				List s;
				s=(Position)malloc(sizeof(struct LNode));
				s->Data=X;
				s->Next=NULL;
				p->Next=s;
				return true;
			}else{
				printf("Wrong Position for Insertion\n");
				return false;
			}
		}
		p=p->Next;
		p1=p1->Next;
	}
//	printf("Wrong Position for Insertion\n");
//	return false;
}
bool Delete( List L, Position P ){
	if(L->Next==NULL){
		printf("Wrong Position for Deletion\n");
		return false;
	}
	List p=L->Next;
	List p1=L;
	while(p){
		if(p==P){
			p1->Next=p->Next;
			return true;
		}
		if(p->Next==NULL){
			printf("Wrong Position for Deletion\n");
			return false;
		}
		p=p->Next;
		p1=p1->Next;
	}
//	printf("Wrong Position for Deletion\n");
//	return false;
}

3.递增的整数序列链表的插入

题意

本题要求实现一个函数,在递增的整数序列链表(带头结点)中插入一个新整数,并保持该序列的有序性。

L是给定的带头结点的单链表,其结点存储的数据是递增有序的;函数Insert要将X插入L,并保持该序列的有序性,返回插入后的链表头指针。

裁判测试程序样例

#include <stdio.h>
#include <stdlib.h>

typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
    ElementType Data;
    PtrToNode   Next;
};
typedef PtrToNode List;

List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表 */

List Insert( List L, ElementType X );

int main()
{
    List L;
    ElementType X;
    L = Read();
    scanf("%d", &X);
    L = Insert(L, X);
    Print(L);
    return 0;
}

/* 你的代码将被嵌在这里 */

样例输入

5
1 2 4 5 6
3

样例输出

1 2 3 4 5 6 

程序代码

List Insert( List L, ElementType X ){
//	List p=L;
//	while(p->Next!=NULL){
//		p=p->Next;
//	}
//	List s;
//	s=(List)malloc(sizeof(struct Node));
//	s->Data=X;
//	s->Next=NULL;
//	p->Next=s;
//	return L;
	if(L->Next==NULL){
		List s;
		s=(List)malloc(sizeof(struct Node));
		s->Data=X;
		s->Next=NULL;
		L->Next=s;
		return L;
	}
	List p=L->Next;
	List p1=L;
	while((p->Data)<X){
		if((p->Next)!=NULL){
			p=p->Next;
			p1=p1->Next;
		}else{
			List s;
			s=(List)malloc(sizeof(struct Node));
			s->Data=X;
			s->Next=NULL;
			p->Next=s;
			return L;
		}
	}
	List s1;
	s1=(List)malloc(sizeof(struct Node));
	s1->Data=X;
	s1->Next=p;
	p1->Next=s1;
	return L;
}

4. 一元多项式的乘法与加法运算

题意

设计函数分别求两个一元多项式的乘积与和。

输入

输入分2行,每行分别先给出多项式非零项的个数,再以指数递降方式输入一个多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。

输出

输出分2行,分别以指数递降方式输出乘积多项式以及和多项式非零项的系数和指数。数字间以空格分隔,但结尾不能有多余空格。零多项式应输出0 0。

样例输入

4 3 4 -5 2  6 1  -2 0
3 5 20  -7 4  3 1

样例输出

15 24 -25 22 30 21 -10 20 -21 8 35 6 -33 5 14 4 -15 3 18 2 -6 1
5 20 -4 4 -5 2 9 1 -2 0

解题思路

加法之前上课的时候讲过,所以比较好写,但是乘法就比较麻烦,除了写一个乘法,还要给他写一个加法。乘法中temp我只定义了,但是没有给他空间,所以一直出错,后来问了别人才知道,忘记给他空间了,加了空间之后最后一个测试点还是错了,最后一个测试点应该是两个空链表,所以还要特判一下才过了。

程序代码

#include<bits/stdc++.h> 
using namespace std;
struct node{
	int xi;	//系数 
	int zhi;	//指数 
	struct node *next;	//指向下一个 
};
//创建链表 -带头节点 
node *creat(int n){
	node *head=NULL;	//创建头节点 
	head=(node *)malloc(sizeof(node));
	head->next=NULL;
	node *p1=NULL;
	node *p2=head;
	for(int i=1;i<=n;i++){
		int a,b;
		cin>>a>>b;
		p1=(node *)malloc(sizeof(node));
		p1->xi=a;
		p1->zhi=b;
		p1->next=NULL;
		p2->next=p1;
		p2=p1;
	}
	return head;
}
//两个链表相加 
node *add(node *pa,node *pb){
	node *p1=pa->next,*p2=pb->next;	//去掉头节点 
	node *p,*q,*r,*pc;
	int x;
	while(p1->zhi==p2->zhi){	//处理前面一直相等的情况 
		x=p1->xi+p2->xi;
      	if(x==0) //系数相加等于0 
 		{
 			if(p1->next&&p2->next){	//p1和p2后面还有结点 
	 			p1=p1->next; 
				p2=p2->next;  
			}else{
				return NULL;	//否则返回NULL 
			}
		}
     	else
		{
			p1->xi=x;
			p2=p2->next;
        } 
	}
	
	if((p1->zhi)<(p2->zhi)){	//使p1的第一个系数是最大的 
		node *tmp=p1;
		p1=p2;
		p2=tmp;
	}
	p=p1->next; 
	q=p2;	
	r=p1;
	pc=p1;  //记录头结点 
	
	//输出p1和p2看是否交换成功 
//	while(p1){
//		if(p1->next)
//			printf("%d %d ",p1->xi,p1->zhi);
//		else{
//			printf("%d %d\n",p1->xi,p1->zhi);break;
//		}
//		p1=p1->next;
//	}
//	while(p2){
//		if(p2->next)
//			printf("%d %d ",p2->xi,p2->zhi);
//		else{
//			printf("%d %d\n",p2->xi,p2->zhi);break;
//		}
//		p2=p2->next;
//	}
	 
	while ((p!=NULL) && (q!=NULL)) 
	{
			if(p->zhi==q->zhi) //指数相等 
 			{
		   			x=p->xi+q->xi;
      				if(x==0) 
				 	{ 
					 	p=p->next; 
						q=q->next;  
					}
     				else
				 	{
		 				p->xi=x;
						q=q->next;
                	} 
 			}
			else if(p->zhi>q->zhi) 
			{ 
				r->next=p; 
				r=p; 
				p=p->next;
    		}
   			else{ 
		   		r->next=q; 
			   	r=q; 
			   	q=q->next;
      		}
       	}  /*  while  end  */
       
    if(p==NULL) //把剩余的加在r后面 
 		r->next=q;  
    else 
 		r->next=p;
	return(pc);
}
//两个链表相乘 
void add_list(node *head,node *t){	//乘法中的加法 
	if(head->next==NULL){	//空的就直接加在后面 
		head->next=t;
		return ;
	}else{
		node *p1=head->next;
		node *p2=head;	//记录p1的前一个结点 
		while(p1){
			if((p1->zhi)==(t->zhi)){
				int x=(p1->xi)+(t->xi);
				if(x==0){
					p2->next=p1->next;
					return ;
				}else{
					p1->xi=x;
					return ;
				}
			}else if((p1->zhi)>(t->zhi)){
				if(p1->next){
					p1=p1->next;
					p2=p2->next;
				}else{
					p1->next=t;
					return ;
				}
			}else{
				p2->next=t;
				t->next=p1;
				return ;
			}
		}
	}
}
node *muli(node *a,node *b){
	if((a->next)==NULL || (b->next)==NULL){	//判断空结点 
		return NULL;
	}
	node *pa=a->next,*pb=b->next,*head;
	head=(node *)malloc(sizeof(node));
	head->next=NULL;
	while(pa){
		node *p1=pb;
		while(p1){
			int t=(pa->xi)*(p1->xi);
			if(t==0){
				if(p1->next){
					p1=p1->next;continue;
				}
				else
					break;
			}else{
				node *temp;
				temp=(node *)malloc(sizeof(node));	//一定要记得给temp一个空间,不然就会出错 
				temp->xi=t;
				temp->zhi=(p1->zhi)+(pa->zhi);
				temp->next=NULL;
				
				add_list(head,temp);
				
//				int cnt=1;
//				node *a=head->next;
//				while(a){
//					printf("%d.%d %d ",cnt++,a->xi,a->zhi);
//					a=a->next;
//				}
//				cout<<endl;
				//printf("1.%d %d\n",head->next->xi,head->next->zhi);
				
			}
			if(p1->next)
				p1=p1->next;
			else
				break;
		}
		if(pa->next)
			pa=pa->next;
		else
			return head;
	}
}

int main(){
	node *pa,*pb,*pc,*pd;
	
	int n;
	cin>>n;
	pa=creat(n);
	
	int m;
	cin>>m;
	pb=creat(m);
	
//	pa=pa->next;
//	while(pa){
//		if(pa->next)
//			printf("%d %d ",pa->xi,pa->zhi);
//		else{
//			printf("%d %d\n",pa->xi,pa->zhi);break;
//		}
//		pa=pa->next;
//	}
//	pb=pb->next;
//	while(pb){
//		if(pb->next)
//			printf("%d %d ",pb->xi,pb->zhi);
//		else{
//			printf("%d %d\n",pb->xi,pb->zhi);break;
//		}
//		pb=pb->next;
//	}

	//判断零多项式 
	if(pa->next==NULL && pb->next==NULL){
		printf("0 0\n");
		printf("0 0\n");
		return 0;
	}else if(pa->next==NULL){
		printf("0 0\n");
		pb=pb->next;
		while(pb){
			if(pb->next)
				printf("%d %d ",pb->xi,pb->zhi);
			else
				printf("%d %d\n",pb->xi,pb->zhi);
			pb=pb->next;
		}
		return 0;
	}else if(pb->next==NULL){
		printf("0 0\n");
		pa=pa->next;
		while(pa){
			if(pa->next)
				printf("%d %d ",pa->xi,pa->zhi);
			else
				printf("%d %d\n",pa->xi,pa->zhi);
			pa=pa->next;
		}
		return 0;
	}

	pc=muli(pa,pb);
	pd=add(pa,pb);
	
	if(pc==NULL){
		printf("0 0\n");
	}else{
		pc=pc->next;	//去掉头结点 
		while(pc){
			if(pc->next)
				printf("%d %d ",pc->xi,pc->zhi);
			else
				printf("%d %d\n",pc->xi,pc->zhi);
			pc=pc->next;
		}
	}
	
	if(pd==NULL){
		cout<<"0 0"<<endl;
	}else{
		while(pd){
			if(pd->next)
				printf("%d %d ",pd->xi,pd->zhi);
			else
				printf("%d %d\n",pd->xi,pd->zhi);
			pd=pd->next;
		}
	}
	return 0;
}

5.两个有序链表序列的交集

题意

已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。

输入

输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。

输出

在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL。

样例输入

1 2 5 -1
2 4 5 8 10 -1

样例输出

2 5

解题思路

因为是有序的所以只用一个个判断下去就行啦。

程序代码

#include<bits/stdc++.h>
using namespace std;
struct node{
	int data;
	struct node *next;
};
node *A,*B,*C;
void Creat(node *A){
	node *p=A;
	int t;
	while((p->next)!=NULL){
		p=p->next;
	}
	while(~scanf("%d",&t)){
		if(t==-1) break;
		node *s=(node *)malloc(sizeof(node));
//		if(s==NULL) return -2;
		s->data=t;
		s->next=NULL;
		p->next=s;
		p=s; 
	}
}
void Add(node *A,node *B,node *C){
	node *pa,*pb,*pc;
	pa=A->next;
	pb=B->next;
	pc=C;
	while(pa&&pb){
		if(pa->data==pb->data){
			pc->next=pa;
			pa=pa->next;
			pb=pb->next;
			pc=pc->next;
		}else if(pa->data<pb->data){
			pa=pa->next;
		}else{
			pb=pb->next;
		}
	}
}
int main(){
	A=(node *)malloc(sizeof(node));
//	if(A==NULL)	return -2;
	A->next=NULL;
	B=(node *)malloc(sizeof(node));
//	if(B==NULL)	return -2;
	B->next=NULL;
	C=(node *)malloc(sizeof(node));
//	if(C==NULL)	return -2;
	C->next=NULL;
	Creat(A);
	Creat(B);
	Add(A,B,C);
	if(C->next==NULL){
		printf("NULL\n");
		return 0;
	}else{
		node *p=C->next;
		while(p){
			if(p->next==NULL){
				printf("%d\n",p->data);
			}else{
				printf("%d ",p->data);
			}
			p=p->next;
		}
	}
	return 0;
}

6.两个有序序列的中位数

题意

已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列A0​​ ,A​1​​ ,⋯,A​N−1​​ 的中位数指A​(N−1)/2​​ 的值,即第⌊(N+1)/2⌋个数(A​0​​ 为第1个数)。

输入

输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。

输出

在一行中输出两个输入序列的并集序列的中位数。

样例输入

5
1 3 5 7 9
2 3 4 5 6

样例输出

4

解题思路

虽然是要求用链表写,但是黑盒测试,所以我就用了数组写啦,嘻嘻。

程序代码

#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
int s1[N],s2[N];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>s1[i];
	for(int i=1;i<=n;i++)
		cin>>s2[i];
	int t=0;
	int j=1,k=1;
	for(int i=1;i<2*n/2+1;i++){
		if(s1[j]<=s2[k]){
			t=s1[j];
			j++;
		}else{
			t=s2[k];
			k++;
		}
	}
	cout<<t<<endl;
	return 0;
}

三、Vjudge

1.最短路(四种解法)

题意

在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

输入

输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。

输出

对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

样例输入

2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

样例输出

3
2

解题思路

这就是一个简单的最短路问题,我用了Floyd算法。

程序代码:(Floyd算法)

#include<iostream>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=100+5;
int g[N][N];
int n,m;
void floyd(){
	int s=1;
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			if(g[i][k]!=INF){
				for(int j=1;j<=n;j++){
					if(g[i][j]>g[i][k]+g[k][j]){
						g[i][j]=g[i][k]+g[k][j];
					}
				}
			}
		}
	}
	printf("%d\n",g[s][n]);
}
int main(){
	while(~scanf("%d %d",&n,&m)){
		if(n==0&&m==0) break;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				g[i][j]=INF;
			}
		}
		for(int i=1;i<=m;i++){
			int a,b,c;
			cin>>a>>b>>c;
			g[a][b]=g[b][a]=c;
		}
		floyd();
	}
	return 0;
}

程序代码:(Bellman_Ford算法)

#include<iostream>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=100+5;
struct node{
	int u,v,w;	//起点、终点、权值 
}g[10005];
//int pre[N];	//记录前驱结点 
int n,m,cnt;
//打印从s到t的最短路径 
//void print_path(int s,int t){
//	if(s==t){
//		printf("%d",s);	//打印起点 
//		return ;
//	}
//	print_path(s,pre[t]);	//先打印前一个点 
//	printf("%d",t);
//}
void Bellman(){
	int s=1;	//起点 
	int d[N];	//记录第i个结点到起点s的最短距离 
	memset(d,INF,sizeof(d));	//初始化 
	d[s]=0;
	for(int k=1;k<=n;k++){	//n轮操作 
		for(int i=0;i<cnt;i++){	//检查每一条边 
			int x=g[i].u,y=g[i].v;
			if(d[x]>d[y]+g[i].w){
				d[x]=d[y]+g[i].w;
//				pre[x]=y;	//记录路径 
			}
		}
	}
	printf("%d\n",d[n]);
//	print_path(s,n);	//打印路径 
}
int main(){
	while(~scanf("%d %d",&n,&m)){
		if(n==0&&m==0) break;
		cnt=0;	//记录边的数量 
		for(int i=1;i<=m;i++){
			int a,b,c;
			cin>>a>>b>>c;
			g[cnt].u=a;g[cnt].v=b;g[cnt].w=c;cnt++;
			g[cnt].u=b;g[cnt].v=a;g[cnt].w=c;cnt++;
		}
		Bellman();
	}
	return 0;
}

程序代码:(SPFA)

#include<bits/stdc++.h>
using namespace std;
const int INF=1e6;
const int N=105;
struct edge{
	int from,to,w;	//起点、终点、权值 
	edge(int a,int b,int c){
		from=a;
		to=b;
		w=c;
	}
};
vector<edge>g[N];	//存第i个结点连接的所有边 
int n,m;
//int pre[N];	//记录前驱结点 
//打印从s到t的最短路径 
//void print_path(int s,int t){
//	if(s==t){
//		printf("%d",s);	//打印起点 
//		return ;
//	}
//	print_path(s,pre[t]);	//先打印前一个点 
//	printf("%d",t);
//}
int spfa(int s){
	int dis[N];	//记录所有结点到起点的距离 
	bool inq[N];	//inq[i]=true表示结点i在队列中 
	int Neg[N];	//判断负圈 
	memset(Neg,0,sizeof(Neg));
	Neg[s]=1;
	memset(dis,INF,sizeof(dis));
	memset(inq,false,sizeof(inq));
	dis[s]=0;	//起点到自己的距离为0 
	queue<int>Q;
	Q.push(s);
	inq[s]=true;	//起点进队列 
	while(!Q.empty()){
		int u=Q.front();
		Q.pop();	//队头出队 
		inq[u]=false;
		for(int i=0;i<g[u].size();i++){	//检查结点u的所有邻居 
			int v=g[u][i].to,w=g[u][i].w;
			if(dis[u]+w<dis[v]){	//u的第i个邻居v,它借道u,到s更近 
				dis[v]=dis[u]+w;	//更新第i个邻居到s的距离 
//				pre[v]=u;	//记录路径 
				if(!inq[v]){	//第i个邻居更新状态了,但是它不在队列中,把它放进队列 
					inq[v]=true;
					Q.push(v);
					Neg[v]++;
					if(Neg[v]>n)return 1;	//出现负圈 
				}
			}
		}
	}
	printf("%d\n",dis[n]);
//	print_path(s,n);
	return 0;
}
int main(){
	while(~scanf("%d %d",&n,&m)){
		if(n==0&&m==0) break;
		for(int i=1;i<=n;i++){
			g[i].clear();
		}
		for(int i=1;i<=m;i++){
			int a,b,c;
			cin>>a>>b>>c;
			g[a].push_back(edge(a,b,c));	//结点a的邻居,都放在node[a]里 
			g[b].push_back(edge(b,a,c));
		}
		spfa(1);
	}
	return 0;
} 

程序代码:(Dijkstra)

#include<bits/stdc++.h>
using namespace std;
const int INF=1e6;
const int N=105;
struct edge{
	int from,to,w;	//起点、终点、权值 
	edge(int a,int b,int c){
		from=a;
		to=b;
		w=c;
	}
};
vector<edge>g[N];	//存图 
struct s_node{
	int id,n_dis;	//结点和这个结点到起点的距离 
	s_node(int b,int c){
		id=b;
		n_dis=c;
	}
	bool operator<(const s_node &a)const{
		return n_dis>a.n_dis;
	}
};
int n,m;
//int pre[N];	//记录前驱结点 
//打印从s到t的最短路径 
//void print_path(int s,int t){
//	if(s==t){
//		printf("%d",s);	//打印起点 
//		return ;
//	}
//	print_path(s,pre[t]);	//先打印前一个点 
//	printf("%d",t);
//}
void dijkstra(){
	int s=1;	//起点s是1 
	int dis[N];	//记录所有结点到起点的距离 
	bool done[N];	//done[i]=true表示结点i的最短路径已经找到 
	memset(dis,INF,sizeof(dis));
	memset(done,false,sizeof(done));
	dis[s]=0;	//起点到自己的距离为0 
	priority_queue<s_node>Q;	//优先队列,存结点信息 
	Q.push(s_node(s,dis[s]));	//起点进队列 
	while(!Q.empty()){
		s_node u=Q.top();
		Q.pop();	//出距起点s距离最小的结点u 
		if(done[u.id])	//丢弃已经找到最短路径的结点,即集合A中的结点 
			continue;
		done[u.id]=true;
		for(int i=0;i<g[u.id].size();i++){	//检查结点u的所有邻居 
			edge y=g[u.id][i];	//u.id的第i个邻居是y.to 
			if(done[y.to])	//丢弃已经找到最短路径的邻居结点 
				continue;
			if(dis[y.to]>y.w+u.n_dis){
				dis[y.to]=y.w+u.n_dis;
				Q.push(s_node(y.to,dis[y.to]));	//扩展新的邻居,放到优先队列中 
//				pre[y.to]=u.id;
			}
		}
	}
	printf("%d\n",dis[n]);
//	print_path(s,n);
}
int main(){
	while(~scanf("%d %d",&n,&m)){
		if(n==0&&m==0) break;
		for(int i=1;i<=n;i++){
			g[i].clear();
		}
		for(int i=1;i<=m;i++){
			int a,b,c;
			cin>>a>>b>>c;
			g[a].push_back(edge(a,b,c));	//结点a的邻居,都放在node[a]里 
			g[b].push_back(edge(b,a,c));
		}
		dijkstra();
	}
	return 0;
} 

四、Wlacm

1.最短路径问题

题意

平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。

若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的

任务是找出从一点到另一点之间的最短路径。

输入

输入共n+m+3行,其中:

第一行为整数n。

第2行到第n+1行(共n行) ,每行两个整数x和y,描述了一个点的坐标。

第n+2行为一个整数m,表示图中连线的个数。

此后的m 行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线。

最后一行:两个整数s和t,分别表示源点和目标点。

输出

输出仅一行,一个实数(保留两位小数),表示从s到t的最短路径长度。

样例输入

5 
0 0 
2 0 
2 2 
0 2 
3 1 
5 
1 2 
1 3 
1 4 
2 5 
3 5 
1 5 

样例输出

3.41

解题思路

就是普通的最短路问题,用Floyd算法就过了,应该数据不是很大.
程序代码

#include<bits/stdc++.h>
#include<cmath>
using namespace std;
const double INF=0x3f3f3f3f;
struct node{
    int x,y;
}a[100+5];
double g[100+5][100+5];
int n,m;
void floyd(int s,int t){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            if(g[i][k]!=INF){
                for(int j=1;j<=n;j++){
                    if(g[i][j]>g[i][k]+g[k][j]){
                        g[i][j]=g[i][k]+g[k][j];
                    }
                }
            }
        }
    }
    printf("%.2lf\n",g[t][s]);
    //printf("%.2lf\n",g[s][t]);
}
int main(){
    //memset(g,INF,sizeof(g));
//  int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d %d",&a[i].x,&a[i].y);
    }
//  for(int i=1;i<=n;i++){
//      printf("%d %d",a[i].x,a[i].y);
//  }
//  int m;
    scanf("%d",&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=INF;
        }
    }
    for(int i=1;i<=m;i++){
        int t1,t2;
        scanf("%d %d",&t1,&t2);
        //g[t1][t2]=g[t2][t1]=1.0*(a[t1].x-a[t2].x)*(a[t1].x-a[t2].x)+1.0*(a[t1].y-a[t2].y)*(a[t1].y-a[t2].y);
        g[t1][t2]=g[t2][t1]=sqrt(1.0*(a[t1].x-a[t2].x)*(a[t1].x-a[t2].x)+1.0*(a[t1].y-a[t2].y)*(a[t1].y-a[t2].y));
        //printf("g=%0.2lf\n",g[t1][t2]);
    }
    int s,t;
    scanf("%d %d",&s,&t);
    floyd(s,t);
    return 0;
}

2.信使

题意

战争时期,前线有n个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。指挥部设在第一个哨所。当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。直至所有n个哨所全部接到命令后,送信才算成功。因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他k个哨所有通信联系的话,这个哨所内至少会配备k个信使)。
现在总指挥请你编一个程序,计算出完成整个送信过程最短需要多少时间。

输入

输入第1行有两个整数n和m,中间用1个空格隔开,分别表示有n个哨所和m条通信线路。1<=n<=100。

第2至m+1行:每行三个整数i、j、k,中间用1个空格隔开,表示第i个和第j个哨所之间存在通信线路,且这条线路要花费k天。

输出

输出仅一个整数,表示完成整个送信过程的最短时间。如果不是所有的哨所都能收到信,就输出-1。

样例输入

4 4
1 2 4
2 3 7
2 4 1
3 4 6

样例输出

11

解题思路

和上一题一样.

程序代码

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f; 
int g[100+10][100+10];
int n,m;
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			if(g[i][k]!=INF){
				for(int j=1;j<=n;j++){
					if(g[i][j]>g[i][k]+g[k][j]){
						g[i][j]=g[i][k]+g[k][j];
					}
				}
			}
		}
	}
}
int main(){
	memset(g,INF,sizeof(g));
	scanf("%d %d",&n,&m);
	int tag=0;
	for(int i=1;i<=m;i++){
		int t1,t2,t3;
		scanf("%d %d %d",&t1,&t2,&t3);
		g[t1][t2]=g[t2][t1]=t3;
	}
	floyd();
	int s=0;
	for(int i=2;i<=n;i++){
		s=max(s,g[1][i]);
		if(g[1][i]==INF){
			tag=1;
			break;
		}
	}
	if(tag){
		printf("-1\n");
	}else{
		printf("%d\n",s);
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值