T1 最短路上的统计
考点:Floyd+暴力
我是真的没想到这题竟然是如此的暴力
首先,要跑一个 Floyd ,只是母庸置疑的
问题是如何求最短路上的点
现在假设 k k k 点是 i i i 点到 j j j 点的最短路上的一个点,那么,显然有 F [ i ] [ j ] = F [ i ] [ k ] + F [ k ] [ j ] F[\ i\ ][\ j\ ]=F[\ i\ ][\ k\ ]+F[\ k\ ][\ j\ ] F[ i ][ j ]=F[ i ][ k ]+F[ k ][ j ]
很好想:因为 k k k 点是 i i i 点到 j j j 点的最短路上的一个点,自然,我们从 i i i 点到 j j j 点会经过 k k k 点,从 i i i 点到 k k k 点的最短路和从 k k k 点到 j j j 点的最短路就必然是从 i i i 点到 j j j 点的最短路
那就也没什么好说的了
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,p,a[105][105],x,y;
int ans[105][105];
void Floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=i==j?0:0x3f3f3f3f;
}
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
a[x][y]=a[y][x]=1;
}
Floyd(); //以上所有全是 Floyd 模板
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
if(a[i][j]==a[i][k]+a[k][j]){ //上文已提,当 k 点是在 i 点到 j 点的最短路上
ans[i][j]++;
}
}
}
}
scanf("%d",&p);
for(int i=1;i<=p;i++){
scanf("%d%d",&x,&y);
printf("%d\n",ans[x][y]); //对应输出
}
return 0;
}
T2 ことりのおやつ
考点:Dijkstra
基本上是板题,但是在判断条件时需要增加一点
因为我们在到达该点时不能超过其极限雪量,所以,需要用到达该点的时间乘上每一秒增加的雪量再加上初始雪量与极限雪量进行比较,若大于了极限雪量,就不能到,反之,就可以到
于是,你开开心心写完了代码,交给了评测姬
问题出在哪了?
不考虑点心店和 ことり 家的雪
真的,审题真的很!重!要!
那么,我们就很容易得到 AC 代码了
#include<queue>
#include<cstdio>
using namespace std;
priority_queue<pair<long long int,int> > q;
const int N=10000005;
long long int head[N],Next[N],ver[N],edge[N],len;
long long int dis[N];
long long int vis[N];
long long int l[N],h[N];
long long int n,m,Start,End,x,y,z,qq,p;
void add(int x,int y,int z){
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
void Dijkstra(){
for(int i=1;i<=n;i++){
dis[i]=0x7f7f7f7f;
}
dis[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis[xx]){
continue;
}
vis[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis[yy]>dis[xx]+zz&&((dis[xx]+zz)*p+l[yy]<=h[yy])){
//可以更新最短路,且不超过极限雪量或者即将到达终点
dis[yy]=dis[xx]+zz;
q.push(make_pair(-dis[yy],yy));
}
}
}
}
int read(){
long long int a=1,b=0;
char ch=getchar();
while(!(ch>='0'&&ch<='9')){
if(ch=='-'){
a=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
b=(b<<1)+(b<<3)+ch-'0';
ch=getchar();
}
return a*b;
}
int main(){
n=read(),m=read(),Start=read(),End=read(),qq=read(),p=read();
for(int i=1;i<=n;i++){
l[i]=read(),h[i]=read();
}
for(int i=1;i<=m;i++){
x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
Dijkstra();
if(dis[End]>qq||dis[End]==0x7f7f7f7f){
printf("wtnap wa kotori no oyatsu desu!");
}else{
printf("%lld",dis[End]);
}
return 0;
}
//剩下的全是 Dijkstra 模板
没有做对的蒟蒻很想扇自己两巴掌
T3 「CQOI2005」新年好
从此以后,新年再也不好
考点:Dijkstra+搜索
我们将 5 5 5 个亲戚分别编号为 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5
对于佳佳而言,他拜访的顺序有 120 120 120 种: 1 → 2 → 3 → 4 → 5 , 1 → 2 → 3 → 5 → 4 , 1 → 2 → 4 → 3 → 5 ⋯ 1\to2\to3\to4\to5,1\to2\to3\to5\to4,1\to2\to4\to3\to5\cdots 1→2→3→4→5,1→2→3→5→4,1→2→4→3→5⋯
是的,全排列
那么,对于求最短路,我们要求六次最短路:分别是以佳佳为起点, 1 1 1 号亲戚为起点, 2 2 2 号亲戚为起点, 3 3 3 号亲戚为起点, 4 4 4 号亲戚为起点, 5 5 5 号亲戚为起点
求的最短路后,再通过一个简单的 DFS 求最小的即可
注意!注意!
由于本蒟蒻做法过于打表,请慎重食用!!!
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
priority_queue<pair<int,int> > q;
const int N=200005;
int head[N],Next[N],ver[N],edge[N],len;
int dis1[N],vis1[N];
int dis2[N],vis2[N];
int dis3[N],vis3[N];
int dis4[N],vis4[N];
int dis5[N],vis5[N];
int dis6[N],vis6[N];
int n,m,x,y,z;
int a[10];
void add(int x,int y,int z){
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
void Dijkstra1(int Start){ //以佳佳为起点的最短路
for(int i=1;i<=n;i++){
dis1[i]=0x3f3f3f3f;
}
dis1[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis1[xx]){
continue;
}
vis1[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis1[yy]>dis1[xx]+zz){
dis1[yy]=dis1[xx]+zz;
q.push(make_pair(-dis1[yy],yy));
}
}
}
}
void Dijkstra2(int Start){ //以 1 号亲戚为起点的最短路
for(int i=1;i<=n;i++){
dis2[i]=0x3f3f3f3f;
}
dis2[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis2[xx]){
continue;
}
vis2[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis2[yy]>dis2[xx]+zz){
dis2[yy]=dis2[xx]+zz;
q.push(make_pair(-dis2[yy],yy));
}
}
}
}
void Dijkstra3(int Start){ //以 2 号亲戚为起点的最短路
for(int i=1;i<=n;i++){
dis3[i]=0x3f3f3f3f;
}
dis3[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis3[xx]){
continue;
}
vis3[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis3[yy]>dis3[xx]+zz){
dis3[yy]=dis3[xx]+zz;
q.push(make_pair(-dis3[yy],yy));
}
}
}
}
void Dijkstra4(int Start){ //以 3 号亲戚为起点的最短路
for(int i=1;i<=n;i++){
dis4[i]=0x3f3f3f3f;
}
dis4[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis4[xx]){
continue;
}
vis4[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis4[yy]>dis4[xx]+zz){
dis4[yy]=dis4[xx]+zz;
q.push(make_pair(-dis4[yy],yy));
}
}
}
}
void Dijkstra5(int Start){ //以 4 号亲戚为起点的最短路
for(int i=1;i<=n;i++){
dis5[i]=0x3f3f3f3f;
}
dis5[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis5[xx]){
continue;
}
vis5[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis5[yy]>dis5[xx]+zz){
dis5[yy]=dis5[xx]+zz;
q.push(make_pair(-dis5[yy],yy));
}
}
}
}
void Dijkstra6(int Start){ //以 5 号亲戚为起点的最短路
for(int i=1;i<=n;i++){
dis6[i]=0x3f3f3f3f;
}
dis6[Start]=0;
q.push(make_pair(0,Start));
while(!q.empty()){
int xx=q.top().second;
q.pop();
if(vis6[xx]){
continue;
}
vis6[xx]=1;
for(int i=head[xx];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis6[yy]>dis6[xx]+zz){
dis6[yy]=dis6[xx]+zz;
q.push(make_pair(-dis6[yy],yy));
}
}
}
}
int read(){
int a=1,b=0;
char ch=getchar();
while(!(ch>='0'&&ch<='9')){
if(ch=='-'){
a=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
b=(b<<1)+(b<<3)+ch-'0';
ch=getchar();
}
return a*b;
} //快读
int main(){
n=read(),m=read(),a[1]=read(),a[2]=read(),a[3]=read(),a[4]=read(),a[5]=read();
a[0]=1;
for(int i=1;i<=m;i++){
x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}
int ans=2147483647;
Dijkstra1(a[0]);
Dijkstra2(a[1]);
Dijkstra3(a[2]);
Dijkstra4(a[3]);
Dijkstra5(a[4]);
Dijkstra6(a[5]); //分别跑最短路
for(int aa=1;aa<=5;aa++){
for(int bb=1;bb<=5;bb++){
for(int cc=1;cc<=5;cc++){
for(int dd=1;dd<=5;dd++){
for(int ee=1;ee<=5;ee++){
if(aa==bb||aa==cc||aa==dd||aa==ee||bb==cc||bb==dd||bb==ee||cc==dd||cc==ee||dd==ee){
continue;
} //枚举全排列
int sum=0;
sum+=dis1[a[aa]]; //将以佳佳家为起点到第一个亲戚家的最短路累加给答案
if(aa==1){ //从佳佳到了 1 号亲戚家
sum+=dis2[a[bb]]; //将以 1 号亲戚家为起点到下一个亲戚家的最短路累加给答案
}else if(aa==2){ //从佳佳到了 2 号亲戚家
sum+=dis3[a[bb]]; //将以 2 号亲戚家为起点到下一个亲戚家的最短路累加给答案
}else if(aa==3){ //从佳佳到了 3 号亲戚家
sum+=dis4[a[bb]]; //将以 3 号亲戚家为起点到下一个亲戚家的最短路累加给答案
}else if(aa==4){ //从佳佳到了 4 号亲戚家
sum+=dis5[a[bb]]; //将以 4 号亲戚家为起点到下一个亲戚家的最短路累加给答案
}else if(aa==5){ //从佳佳到了 5 号亲戚家
sum+=dis6[a[bb]]; //将以 5 号亲戚家为起点到下一个亲戚家的最短路累加给答案
}
if(bb==1){ //以下做法同上
sum+=dis2[a[cc]];
}else if(bb==2){
sum+=dis3[a[cc]];
}else if(bb==3){
sum+=dis4[a[cc]];
}else if(bb==4){
sum+=dis5[a[cc]];
}else if(bb==5){
sum+=dis6[a[cc]];
}
if(cc==1){
sum+=dis2[a[dd]];
}else if(cc==2){
sum+=dis3[a[dd]];
}else if(cc==3){
sum+=dis4[a[dd]];
}else if(cc==4){
sum+=dis5[a[dd]];
}else if(cc==5){
sum+=dis6[a[dd]];
}
if(dd==1){
sum+=dis2[a[ee]];
}else if(dd==2){
sum+=dis3[a[ee]];
}else if(dd==3){
sum+=dis4[a[ee]];
}else if(dd==4){
sum+=dis5[a[ee]];
}else if(dd==5){
sum+=dis6[a[ee]];
}
ans=min(ans,sum);
}
}
}
}
}
printf("%d",ans); //输出答案
return 0;
}
我敢打赌,当年省选肯定有人做法和我一样!绝对有!
T4 算法导论
Tips:由于标程有误,85 分代码即为 100 分
考点:次短路
最短路大家都会,那次短路呢
还记得在很久很久以前,我们是如何求次小数的呢?
我们可以定义最小数 m i n n minn minn 和次小数 m i n n n minnn minnn ,在遍历每一个元素时,如果它比最小数还要小,就把最小数的值赋给次短路,再把该元素的值赋给最小数;如果它在最小数与次小数之间,则只更新次小数;否则什么也不做
我们可以用类似的思想解决次短路
#include<queue>
#include<cstdio>
using namespace std;
const int N=1000005;
int head[N],Next[N],ver[N],len;
long long int edge[N];
long long int dis[N][2]; //dis[i][0] 表示以 i 为终点的最短路,dis[i][1] 表示以 i 为终点的次短路
int n,m,Start,End,x,y;
long long int z,zz;
struct node{
int kkksc03,num,sum;
}nd;
queue<node> q;
void add(int x,int y,long long int z){
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
void Dijkstra(){
for(int i=1;i<=n;i++){
dis[i][0]=dis[i][1]=0x3f3f3f3f;
}
dis[Start][0]=0;
nd.kkksc03=0,nd.num=Start,nd.sum=0;
q.push(nd);
while(!q.empty()){
nd.kkksc03=q.front().kkksc03,nd.num=q.front().num,nd.sum=q.front().sum;
int KKKSC03=nd.kkksc03,NUM=nd.num;
long long int SUM=nd.sum;
q.pop();
if(dis[NUM][KKKSC03]!=SUM){ //如果不相等,说明已经被更新过了,可代替 vis 数组
continue;
}
for(int i=head[NUM];i;i=Next[i]){
int yy=ver[i],zz=edge[i];
if(dis[yy][1]<=dis[NUM][KKKSC03]+zz){ //如果当前路径权值比次短路大
continue; //不理
}
if(dis[yy][0]<=dis[NUM][KKKSC03]+zz){ //如果当前路径权值在最短路与次短路之间
dis[yy][1]=dis[NUM][KKKSC03]+zz; //只更新次短路
nd.kkksc03=1,nd.num=yy,nd.sum=dis[yy][1]; //相应压栈操作
q.push(nd);
}else{ //如果当前路径权值比最短路小
dis[yy][1]=dis[yy][0]; //把最短路的值赋给次短路
nd.kkksc03=1,nd.num=yy,nd.sum=dis[yy][1]; //相应压栈操作
q.push(nd);
dis[yy][0]=dis[NUM][KKKSC03]+zz; //把当前路径权值赋给最短路
nd.kkksc03=0,nd.num=yy,nd.sum=dis[yy][0]; //相应压栈操作
q.push(nd);
}
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&Start,&End);
for(int i=1;i<=m;i++){
scanf("%d%d%lld%lld",&x,&y,&z,&zz);
add(x,y,z*360360/zz); //为避免 double 精度问题,需乘上 [1,15] 的最小公倍数
add(y,x,z*360360/zz);
}
Dijkstra();
printf("%lld",dis[End][1]/360360); //把多乘的 360360 除回来
return 0;
}