2021年暑训最短路专题题解(待补全)

昨天一天才把DJ&SPFA卷明白,麻了麻了 2021-7-15
三天了,各种最短路板子终于敲熟了,入门(√)。
PS:进阶好难www
2021-7-17

2021年暑训最短路专题

Problem A:Poj2387

Til the Cows Come Home
题目大意:已知一个有T条边,N个点的无向图,求从点1到点N的最短路(板子题)
题解:数据量不大,DJ可以不需要优化,当点的数据超过1e4时最好采用堆优化DJ。
AC代码:(堆优化DJ)

#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=2e3+7;
const int inf=0x3f3f3f3f;
struct edge{
    int to,cost;
};
vector<edge> G[maxn];
int d[maxn];
int n,t;
priority_queue<P,vector<P>,greater<P> > que;
void dij(int s){
    fill(d+1,d+1+n,inf);
    d[s]=0;
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost) {
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to],e.to));
            }
        }
    }
}
int main(){
    cin>>t>>n;
    for(int i=1;i<=t;i++){
        int a,b,c;
        cin>>a>>b>>c;
        edge e;
        e.to=b,e.cost=c;
        G[a].push_back(e);
        e.to=a;
        G[b].push_back(e);
    }
    dij(1);
    cout<<d[n];
    return 0;
}

Problem B:Poj2253

Frogger
题目大意:青蛙A在第一块石头,青蛙B在第二块石头,旁边还有其他石头,给出每块石头的坐标,青蛙A有很多种方案到达第二块石头,它想知道所有路径中最大的最短距离。
题解:(堆优化DJ)这道题难在无法理解如何松弛,在求最短路的时候,d[x]数组保存的是从源点到达点x路径中最大的最短距离,由于是求最短距离,
我们的堆要是小顶堆,这样才能保证每次取出的路径是最短的,在此基础上对每个端点进行松弛(d[i]=min(d[i],max(d[v],e.cost))),尽量让最短的边达到最大。
AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<math.h>
#include<iomanip>
using namespace std;
typedef pair<int,double> P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
   int to;
   double cost;
};
vector<edge>G[maxn];
int n;
double x[202],y[202];//石块坐标
double d[maxn];
void dij(int s){
    fill(d+1,d+1+n,inf);
    d[s]=0;
    priority_queue<P,vector<P>,greater<P> >que;//小根堆
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]>max(d[v],e.cost)){//松弛操作//针对于inf,将d[x]=inf转化为max(d[v],e.cost)
                d[e.to]=max(d[v],e.cost);
                que.push(P(d[e.to],e.to));//处理完所有点后得到的d[n]就是我们要求的最大的最小值
            }
        }
    }
}
int main(){
    int cnt=1;
    while(cin>>n&&n){
        for(int i=1;i<=202;i++)G[i].clear();
        for(int i=1;i<=n;i++){
            cin>>x[i]>>y[i];
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                double dis=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
                edge e;
                e.to=j;
                e.cost=dis;
                G[i].push_back(e);
            }
        }//邻接表存图
        dij(1);
        cout<<"Scenario #"<<cnt++<<endl<<"Frog Distance = ";
        cout<<fixed<<setprecision(3)<<d[2]<<endl<<endl;
    }
}

Problem C: Poj1797

Heavy Transportation
题目大意:与B类似,求所有路径中最小的最大值。
题解:与B类似,d[x]数组初始化为0,d[s]初始化为inf,优先队列选用大根堆,松弛方程为d[e.to]=max(d[e.to],min(d[v],e.cost));
AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<stdio.h>
using namespace std;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
typedef pair<int,int>P;
int t,n,m;
struct edge {
    int to,cost;
};
vector<edge> G[maxn];
int d[maxn];
void dj(int s){
    fill(d+1,d+1+n,0);
    d[s]=inf;
    priority_queue<P,vector<P>,less<P> >que;
    que.push(P(inf,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]<min(d[v],e.cost)){
                d[e.to]=min(d[v],e.cost);
                que.push(P(d[e.to],e.to));
            }
        }
    }
}
int main(){
    scanf("%d",&t);
    int cnt=1;
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            edge e;
            e.to=a,e.cost=c;
            G[b].push_back(e);
            e.to=b;
            G[a].push_back(e);
        }
        dj(1);
        printf("Scenario #%d:\n%d\n\n",cnt++,d[n]);
        for(int i=1;i<=n;i++)G[i].clear();
    }
    return 0;
}

Problem D: Poj3268

Silver Cow Party
题目大意:N个农场,M条路(单向边),其中编号为X的农场要举行party,求其他农场的cow们中往返时间最长的那只的往返时间。
题解:自己画张图很容易证明a到b的最短路,可以由b到a所求最短路的矩阵的转置所求出。所以本题两次Dj即可完成。
AC代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
    int to,cost;
};
vector<edge>G1[maxn],G2[maxn];//两张图,G2是G1的转置
int n,m,x,d1[maxn],d2[maxn];//d1和d2分别是G1和G2图中所求最短路
void dj1(int s){//G1的最短路
    fill(d1+1,d1+1+n,inf);
    d1[s]=0;
    priority_queue<P,vector<P>,greater<P> >que;
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d1[v]<p.first)continue;
        for(int i=0;i<G1[v].size();i++){
            edge e=G1[v][i];
            if(d1[e.to]>d1[v]+e.cost){
                d1[e.to]=d1[v]+e.cost;
                que.push(P(d1[e.to],e.to));
            }
        }
    }
}
void dj2(int s){//G2的最短路
    fill(d2+1,d2+1+n,inf);
    d2[s]=0;
    priority_queue<P,vector<P>,greater<P> >que;
    que.push(P(0,s));
    while(!que.empty()){
        P p=que.top();
        que.pop();
        int v=p.second;
        if(d2[v]<p.first)continue;
        for(int i=0;i<G2[v].size();i++){
            edge e=G2[v][i];
            if(d2[e.to]>d2[v]+e.cost){
                d2[e.to]=d2[v]+e.cost;
                que.push(P(d2[e.to],e.to));
            }
        }
    }
}
int main(){
    cin>>n>>m>>x;
    for(int i=1;i<=m;i++){//邻接表存图
        int a,b,c;
        cin>>a>>b>>c;
        edge e;
        e.to=b,e.cost=c;
        G1[a].push_back(e);
        e.to=a;
        G2[b].push_back(e);
    }
    dj1(x);
    dj2(x);
    int ans=0;
    for(int i=1;i<=n;i++){//找出所有cow中花费时间最长的那只
        ans=max(ans,d1[i]+d2[i]);
    }
    cout<<ans;
}

Problem E Poj1860

Currency Exchange
题目大意:已知你是Nike,你家附近有M台货币转换机,可以转换N种货币,你目前有S种类的货币V枚,每台货币转换机具有六个数据:a,b为两种货币的类型,Cab、Rab是a转化为b的汇率及佣金,Cba、Rba是b转化为a的汇率及佣金。
a转化为b的转化公式:设a有k枚,那么可以转化(k-Rab)*Cab枚b。
问:是否有可能在转化(不限次数)后,使自己本金变多?
题解:由于需要增加本金,那么我们需要在这些转换规则中找到一个正环,
利用SPFA解决问题。
AC代码

#include<iostream>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stack>
#include<map>
#include<limits.h>
#include<vector>
#include<string.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e7;
const int M = 1e5+7;
#define INF 1e7+5
#define INM INT_MIN
#define pb(a)  push_back(a)
#define mk(a,b) make_pair(a,b)
#define dbg(x) cout << "now this num is " << x << endl;
#define sd(a) scanf("%d",&a)
#define sld(a) scanf("%lld",&a)
#define sdd(a,b) scanf("%d %d",&a,&b)
#define sddd(a,b,c) scanf("%d %d %d",&a,&b,&c)
#define pr(a) printf("%d\n",a)
#define plr(a) printf("%lld\n",a)
#define pr_(a) printf("%d ",a)
#define _pr(a) printf(" %d",a)
int vis[105],n,m,s;
double dis[105],v;
struct Node
{
    int to;
    double a1,b1;//存转换规则,只存A-B的,因为看成有向边
};
vector<Node> G[105];
bool spfa(int x)
{
    memset(vis,0,sizeof(vis));
    vis[x] = 1;
    for(int i=1;i<=n;++i) dis[i] = 0;//这里应该是找最大路
    dis[x] = v;
    queue<int> Q;
    Q.push(x);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;//消除标记
        for(int i=0;i<G[u].size();++i)
        {
            Node e = G[u][i];
            if(dis[e.to] < (dis[u]-e.b1)*e.a1)
            {
                dis[e.to] = (dis[u]-e.b1)*e.a1;
                if(!vis[e.to])
                {
                    vis[e.to] = 1;//添加标记
                    Q.push(e.to);
                }
            }
            if(dis[x] > v) return true;//判断是否存在正环了
        }
    }
    return false;
}
int main()
{
    sddd(n,m,s);
    scanf("%lf",&v);
    while(m--)
    {
        int a,b;
        double a1,b1,a2,b2;
        sdd(a,b);
        scanf("%lf %lf %lf %lf",&a1,&b1,&a2,&b2);
        Node p,q;
        p.to = b;p.a1 = a1;p.b1 = b1;
        q.to = a;q.a1 = a2;q.b1 = b2;
        G[a].pb(p);
        G[b].pb(q);
    }
    if(spfa(s)) printf("YES\n");
    else printf("NO\n");
}

Problem F: Poj3259

Wormholes
题目大意:一个农夫有N块田,M条路(双向),每条路需要花费一定时间,他的田中还存在W个虫洞,这些虫洞可以将农夫传送回一定时间以前(单向),问:有没有一种方案能使他回到初始点与自己碰面。
题解:虫洞的时间为负,SPFA判断是否存在负环即可。
AC代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;//first为点,second为记录迭代了多少次
const int maxn=5e2+7;
const int inf=0x3f3f3f3f;
int d[maxn];
int n,m,w;
int vis[maxn];
struct edge{
    int to,cost;
};
vector<edge>G[maxn];
bool SPFA(int s){
    int cnt[maxn]={0};
    fill(d+1,d+1+n,inf);
    fill(vis+1,vis+1+n,0);
    d[s]=0;
    vis[s]=1;
    queue<P>que;
    que.push(P(s,cnt[s]));
    while(!que.empty()){
        P p=que.front();
        que.pop();
        int v=p.first;
        vis[v]=0;//点出队,成环时用得到
        //if(p.second>n)return 0;
        for(int i=0;i<G[v].size();i++){//判断与V相邻的点
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost){//松弛
                d[e.to]=d[v]+e.cost;
                if(!vis[e.to]){
                    que.push(P(e.to,++cnt[e.to]));
                    vis[e.to]=1;
                }
            }
        }
        for(int i=1;i<=n;i++){
            if(cnt[i]>n)return 0;
        }
    }
    return 1;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        for(int i=1;i<=maxn;i++)G[i].clear();
        cin>>n>>m>>w;
        for(int i=1;i<=m;i++){
            int a,b,c;
            edge e;
            cin>>a>>b>>c;
            e.to=b,e.cost=c;
            G[a].push_back(e);
            e.to=a;
            G[b].push_back(e);
        }
        for(int i=1;i<=w;i++){
            int a,b,c;
            edge e;
            cin>>a>>b>>c;
            e.to=b,e.cost=-c;
            G[a].push_back(e);
        }
        if(SPFA(1)){
            cout<<"NO"<<endl;
        }
        else cout<<"YES"<<endl;
    }
    return 0;
}

Problem G: Poj1502

MPI Maelstrom
题目大意:已知N个点,给出一个下三角矩阵(由于是无向图,只需要给出下三角矩阵即可),“X”代表无法到达,问从1到其他点最短路中最长的距离是多少。(本题建图很有意思,感兴趣的同学可以自己建一手)
题解:1<=n<=100,由于数据量很弱,选择用Floyd来写,DJ和SPFA都可以写但没必要。Floyd写完遍历源点到其他点的距离取最大值。
AC代码

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
const int maxn=2e5+7;
const int inf=0x3f3f3f3f;
int e[101][101];
int main(){
    int n;
    char s[11];
    cin>>n;
    for(int i = 1;i<=n;i ++){//初始化
        for(int j = 1;j<=n;j ++){
            if(i == j)
                e[i][j] = 0;
            else
                e[i][j] = inf;
        }
    }
    for(int i = 2;i<=n;i ++){
        for(int j = 1;j<i;j ++){
            scanf("%s",s);
            if(strcmp(s,"x") == 0)
                e[i][j] = inf;
            else
                e[i][j] = e[j][i] = atoi(s);//atoi()函数可以将字符串自动转化为数字
        }
    }
    for(int k=1;k<=n;k++){//经典Floyd
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){//取最大值
        ans=max(ans,e[1][i]);
    }
    cout<<ans;
}

Problem H: Poj3660

Cow Contest
题目大意:有N头牛,M个关系,每个关系输入A,B,表示A能打败B,问最多能确定多少头牛的排名。
题解:关键的一点就是:只要一头牛最终的关系网中出现了其他四头牛,那么这头牛的排名便可以确定。1<=n<=100,我们还是可以用Floyd解决问题,将每头牛之间的关系完善(比如A>B&&B>C那么A>C)。
AC代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,a,b;
int G[101][101]={0};
int num[101]={0};
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {//A打败B,G[a][b]置为1
        cin >> a >> b;
        G[a][b] = 1;
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (G[i][k] == 1 && G[k][j] == 1) {只有i打败k,k打败j,i才可以打败j
                    G[i][j] = 1;
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){//选择一头牛
        int cnt=0;
        for(int j=1;j<=n;j++){//查看关系网
            if(G[i][j]||G[j][i])cnt++;
        }
        if(cnt==n-1)ans++;//出现了n-1头牛ans++
    }
    cout<<ans;
}

Problem I:Poj2240

Arbitrage
题目大意:已知N种货币和M种兑换关系,问第一种货币是否可以在经过交易后变多。
题解:将汇率当成边权,Floyd跑一边后看第一个货币经过转换后的汇率是否大于一即可。(又是一个有意思的建图)
AC代码

#include<iostream>
#include<map>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m;
double G[100][100];
map<string,int> name;
int main() {
    int cnt=1;
    while(cin>>n){
        if(n==0)break;
        string s[50];
        for(int i=1;i<=n;i++){
            cin>>s[i];
            name[s[i]]=i;
        }
        cin>>m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j)G[i][j]=1;
                else G[i][j]=0;
            }
        }
        for(int i=1;i<=m;i++){
            string a,b;
            double rat;
            cin>>a>>rat>>b;
            G[name[a]][name[b]]=rat;
        }
        for(int k=1;k<=n;k++){
            for(int i=1;i<=n;i++){
                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 flag=0;
        for(int i=1;i<=n;i++){
            if(G[i][i]>1){
                flag=1;
                break;
            }
        }
        if(flag)cout<<"Case "<<cnt++<<": Yes"<<endl;
        else cout<<"Case "<<cnt++<<": No"<<endl;
    }
}

Problem J:Poj1511

Invitation Cards
题目大意:与Problem B几乎相同,仅仅加了一层n,m的多组输入。
题解:由于数据量较大并且存在多组输入,建议使用链式前向星存图,再使用转置矩阵加双DJ。(板子题就不多说了)
AC代码:(这里我用的是Vector模拟邻接表的,7000ms差点超时)

#include<cstdio>
#include<string.h>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#define maxn 1000001
#define inf 0x3f3f3f3f
typedef pair<int,int> P;
struct edge{
	int f;
	int t;
	int c;
	edge(){	f=0,t=0,c=0;}
	edge(int ff,int tt,int cc){
		f=ff,t=tt,c=cc;
	}
};
int dist[maxn];
vector<edge> map[maxn];
edge edges[maxn];
void dijkstra(int s,int n){
	priority_queue<P,vector<P>,greater<P> > Q;
	for(int i=1;i<=n;i++)
		dist[i]=inf;
	dist[s]=0;
	bool visited[maxn];
	memset(visited ,0,sizeof(visited));
 
	Q.push(P(0,s));
	while(!Q.empty()){
		int v=Q.top().second;
		Q.pop();
		if(visited[v])  continue;
		visited[v]=true;
		for(int i=0;i<map[v].size();i++){
			edge e=map[v][i];
			if(dist[e.t]>dist[v]+e.c){
				dist[e.t]=dist[v]+e.c;
				Q.push(P(dist[e.t],e.t));
			}
		}
	}
}
void init(int n){
	for(int i=0;i<=n;i++){
		map[i].clear();
	}
}
int main(){
	//freopen("in.txt","r",stdin);
	int cases;
	scanf("%d",&cases);
	for(int t=1;t<=cases;t++){
		int n,m;
		scanf("%d %d",&n,&m);
		init(n);
		for(int i=1;i<=m;i++){
			int a,b,c;
			scanf("%d %d %d",&a,&b,&c);
			edges[i]=edge(a,b,c);
			map[a].push_back(edge(a,b,c));
		}
		dijkstra(1,n);
		long long int sum=0;
		for(int i=1;i<=n;i++)
			sum+=dist[i];
		init(n);
		for(int i=1;i<=m;i++){
			edge tmp=edges[i];
			map[tmp.t].push_back(edge(tmp.t,tmp.f,tmp.c));
		}
		dijkstra(1,n);
		for(int i=1;i<=n;i++)
			sum+=dist[i];
		printf("%lld\n",sum);
	}
}

Problem K:Poj3159

Candies
题目大意:N个孩子,M个关系,输入a,b,c代表b的糖果数不会比a多c个以上。(差分约束题)
题解:差分约束板子题,由于求两个孩子间最大的糖果数之差,那我们用SPFA跑一遍最短路。
差分约束不懂的同学请移步至差分约束系统
AC代码:(链式前向星+SPFA)

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=3e4+7;
int head[150009];
int vis[maxn];
int dis[maxn];
struct edge{
    int to,cost,next;
}e[150009];
int t;
int n,m,u,v,w;
void addedge(int v,int w){
    e[t].to=v;
    e[t].cost=w;
    e[t].next=head[u];
    head[u]=t++;
}
void SPFA(int s){
    fill(dis+1,dis+1+n,inf);
    fill(vis+1,vis+1+n,0);
    stack<int>q;
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int v=q.top();
        q.pop();
        vis[v]=0;
        for(int i=head[v];~i;i=e[i].next){
            if(dis[e[i].to]>dis[v]+e[i].cost){
                dis[e[i].to]=dis[v]+e[i].cost;
                if(!vis[e[i].to]){
                    vis[e[i].to]=1;
                    q.push(e[i].to);
                }
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    t=0;
    fill(head,head+1+n,-1);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        addedge(v,w);
    }
    SPFA(1);
    int mx=0;
    for(int i=2;i<=n;i++){
        mx=max(mx,dis[i]);
    }
    printf("%d",mx);
    return 0;
}

Problem L: Poj2502

Subway
题目大意:给你家、学校的坐标,以及几条地铁线,每个地铁线有几个地铁站,给出地铁站的坐标,在地铁线上可以以40km/h的速度行驶,从家到地铁站,从地铁线到地铁线,从地铁站到学校,步行速度为10km/h,问最少多久能从家到学校。(坐标以m为单位)。
题解:难在建图上,每个点之间都要建一条边,建完图后就是一个DJ或者SPFA的板子题了。
AC代码

#include<vector>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<set>
#include<cmath>
#include<cstring>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<double,int> pii;
typedef pair<int,pii> PII;
const int inf = 1<<30;
const int maxn = 1e5+5;
struct Node 
{
    int x,y;
    Node(){}
    Node(int a,int b):x(a),y(b){}
};
Node beg,en;
vector<Node> node;
Node D[maxn];
struct Edge
{
    int from,to;
    double val;
    double cost;
};
vector<Edge> G[maxn];
 
double getval(Node &a,Node& b)
{
    return sqrt((double)(abs(a.x-b.x)*abs(a.x-b.x)+abs(a.y-b.y)*abs(a.y-b.y)));
}
double getcost(double cnt,bool issubway)
{
    double ans = 0;
    if(issubway){
        ans = cnt/40000 * 60;
    }else{
        ans = cnt/10000 * 60;
    }
    return ans;
}
 
double dis[maxn];
bool vis[maxn];
int cnt = 0;
 
double dijkstra()
{
    memset(vis,0,sizeof(vis));
    fill(dis,dis+maxn,inf);
    priority_queue<pii,vector<pii>,greater<pii> > Q;
    Q.push(pii(0,cnt));
    while(!Q.empty()){
        pii t = Q.top();
        Q.pop();
        int u = t.second;
        double d = t.first;
        if(vis[u])continue;
        vis[u] = 1;
        for(int i=0;i<G[u].size();i++){
            Edge e = G[u][i];
            if(e.cost + d < dis[e.to]){
                dis[e.to] = e.cost + d;
                Q.push(pii(dis[e.to],e.to));
            }
        }
    }
    return dis[cnt+1];
}
 
int main(int argc, char const *argv[])
{
    scanf("%d%d",&beg.x,&beg.y);
    scanf("%d%d",&en.x,&en.y);
    int x,y;
    int k = 0;
    Edge e;
    while(~scanf("%d%d",&x,&y)){
        if(x==-1&&y==-1){
            for(int i=k;i<cnt-1;i++){
                e.val = getval(D[i], D[i+1]);
                e.cost = getcost(e.val, 1);
                e.from = i;
                e.to = i+1;
                G[e.from].push_back(e);
                swap(e.from, e.to);
                G[e.from].push_back(e);
            }
            k = cnt;
            continue;
        }
        D[cnt] = Node(x,y);
        cnt++;
    }
    D[cnt] = beg;
    D[cnt+1] = en;
    for(int i=0;i<cnt+2;i++){
        for(int j=i+1;j<cnt+2;j++){
            e.val = getval(D[i], D[j]);
            e.cost = getcost(e.val, 0);
            e.from = i;
            e.to = j;
            G[e.from].push_back(e);
            swap(e.from, e.to);
            G[e.from].push_back(e);
        }
    }
                
    double ans = dijkstra();
    printf("%d\n",(int)(ans+0.5));
    return 0;
}

Problem M: Poj1062

昂贵的聘礼
题目大意:中文题就不需要大意了吧?自己看去欸嘿嘿。
题解:将两件物品之间的转换关系当作权值建图,由于不知道酋长等级,我们枚举每一个可能的等级跑DJ,找到花费最小的那条路。
PS:还有一个建立超级源点的方法,后面学完了会补上来的。
AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<math.h>
#include<string.h>
#define re register
using namespace std;
typedef long long ll;
const double eps=1e-7;
const int INF=1e9;
const int N=105;
int n,m;
int x,y;
int cnt;
int tot;
int a,c;
int dis[N];
bool vis[N];
int level[N];
int mp[N][N];
int dij(int l,int r)
{
    memset(dis,0x3f,sizeof dis);
    memset(vis,0,sizeof vis);
    dis[0]=0;
   // vis[0]=1;
    for(int i=1;i<=n+1;i++)
    {
        int t=-1;
        for(int j=0;j<=n;j++)
          if(!vis[j]&&(t==-1||dis[t]>dis[j])) t=j;
          vis[t]=1;
        for(int j=1;j<=n;j++)
         if(level[j]>=l&&level[j]<=r)
          dis[j]=min(dis[j],dis[t]+mp[t][j]);

    }
    return dis[1];




}
int main()
{

     memset(mp,0x3f,sizeof mp);
    
      cin>>m>>n;
      for(int i=1;i<=n;i++)
      {
          int val,cnt;
          cin>>val>>level[i]>>cnt;
          mp[0][i]=min(mp[0][i],val);
          for(int j=1;j<=cnt;j++)
          {
              int id,w;
              cin>>id>>w;
              mp[id][i]=min(mp[id][i],w);
          }
      }
      int ans=INF;
      for(int i=level[1]-m;i<=level[1];i++) ans=min(ans,dij( i, i+m));
      cout<<ans;

    return 0;

}

Problem N: Poj1847

Tram
题目大意:N个岔路口,我们要从a到b,每个岔路口会给x条路,分别指向对应的岔路口,铁轨初始状态默认打在第一条路上,问最少要操作几次铁轨才能到达?
题解:DJ\SPFA板子题,建图时每个岔路口第一条铁轨默认权值为0,其他的为1,跑一遍DJ\SPFA即可。
AC代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
//typedef pair<int,int>P;
const int inf=0x3f3f3f3f;
const int maxn=1e2+7;
int n,a,b;
int dis[maxn];
int vis[maxn];
int G[maxn][maxn];
void dij(int s){
    fill(dis+1,dis+1+n,inf);
    fill(vis+1,vis+1+n,0);
    dis[s]=0;
    while(1){
        int v=-1;
        for(int i=1;i<=n;i++){
            if(!vis[i]&&(v==-1||dis[v]>dis[i]))v=i;
        }
        if(v==-1)break;
        vis[v]=1;
        for(int i=1;i<=n;i++){
            if(dis[i]>dis[v]+G[v][i]){
                dis[i]=dis[v]+G[v][i];
            }
        }
    }
}
int main() {
    cin>>n>>a>>b;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j)G[i][j]=0;
            else G[i][j]=inf;
        }
    }
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        for(int j=1;j<=x;j++){
            int y;
            cin>>y;
            if(j==1)G[i][y]=0;
            else G[i][y]=1;
        }
    }
    dij(a);
    if(dis[b]>=inf)cout<<"-1"<<endl;
    else cout<<dis[b]<<endl;
}

Problem O: LightOJ1704

Extended Traffic
题目大意:N座城市,每个城市会给出拥挤度Pi,ab两座城市之间花费是(Pb-Pa)^3,问从x到y花费最少的时间是多少。如果不能到达或者时间小于等于3,输出“?”,否则输出所需时间。
题解:SPFA判负环(板子题)。
AC代码

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int>P;
const int inf=0x3f3f3f3f;
const int maxn=2e2+7;
int t,n,m,q;
int busy[202];
struct edge{
    int to,cost;
};
vector<edge>G[maxn];
int d[maxn];
int vis[maxn];
int cnt[maxn]={0};
void SPFA(int s){
    queue<int>q;
    fill(d+1,d+1+n,inf);
    fill(vis+1,vis+1+n,0);
    d[s]=0;
    q.push(s);
    vis[s]=1;
    while(!q.empty()){
        int v=q.front();
        q.pop();
        vis[v]=0;
        for(int i=0;i<G[v].size();i++){
            edge e=G[v][i];
            if(d[e.to]>d[v]+e.cost){
                d[e.to]=d[v]+e.cost;
                if(!vis[v]&&cnt[e.to]<=n){
                    vis[e.to]=1;
                    q.push(e.to);
                    cnt[e.to]++;
                }
            }
        }
    }
}
int main(){
    cin>>t;
    int sum=0;
    while(t--){
        sum++;
        for(int i=1;i<=n;i++)G[i].clear();
        fill(cnt+1,cnt+1+n,0);
        cin>>n;
        for(int i=1;i<=n;i++)cin>>busy[i];
        cin>>m;
        for(int i=1;i<=m;i++){
            int a,b;
            cin>>a>>b;
            edge e;
            e.to=b;
            e.cost=(busy[b]-busy[a])*(busy[b]-busy[a])*(busy[b]-busy[a]);
            G[a].push_back(e);
        }
        SPFA(1);
        cin>>q;
        cout<<"Case "<<sum<<":"<<endl;
        while(q--){
            int x;
            cin>>x;
            if(d[x]!=inf&&d[x]>=3&&cnt[x]<=n)cout<<d[x]<<endl;
            else cout<<"?"<<endl;
        }
    }
    return 0;
}

Problem P:

The Shortest Path in Nya Graph
待补全

Problem Q:

Marriage Match IV
待补全

Problem R:

0 or 1
待补全

Problem S: Poj3169

Layout
题目大意:N个人,ML个喜欢规则,MD个讨厌规则,如果无法建立这个图输出-2,如果第一个人和第N个人之间距离为无穷大则输出-1,否则输出第一个人与第N个人之间的距离。
题解:1、构造差分约束系统:
A,B距离不超过D则B-A<=D,
A,B距离至少为D则A-B<=-D.
2、若有解则求1和N之间的最短路径:
例如:A-B<=D1 , B-C<=D2, A-C<=D3 不等式相加得:A-C<=min(D3,D1+D2)。而当A-B<=D时,我们建的边是B->A的,所以我们只要求出1到N的最短距离即可,如果没有最短距离,则输出-2.
AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e3+7;
int n,ml,md,a,b,c;
int dis[maxn];
int vis[maxn];
int head[20005];
int cnt[maxn];
struct edge{
    int to,cost,next;
}e[20005];
int t;
void addedge(int u,int v,int w){
    e[t].to=v;
    e[t].cost=w;
    e[t].next=head[u];
    head[u]=t++;
}
int SPFA(int s){
    fill(dis,dis+1+n,inf);
    fill(vis,vis+1+n,0);
    fill(cnt,cnt+1+n,0);
    stack<int>q;
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int v=q.top();
        q.pop();
        vis[v]=0;
        cnt[v]++;
        if(cnt[v]>n)return -1;
        for(int i=head[v];~i;i=e[i].next){
            if(dis[e[i].to]>dis[v]+e[i].cost){
                dis[e[i].to]=dis[v]+e[i].cost;
                if(!vis[e[i].to]){
                    vis[e[i].to]=1;
                    q.push(e[i].to);
                }
            }
        }
    }
    if(dis[n]==inf)return -2;
    else return dis[n];
}
int main(){
    cin>>n>>ml>>md;
    fill(head,head+1+n,-1);
    t=0;
    for(int i=1;i<=ml;i++){
        cin>>a>>b>>c;
        addedge(a,b,c);
    }
    for(int i=1;i<=md;i++){
        cin>>a>>b>>c;
        addedge(b,a,-c);
    }
    cout<<SPFA(1)<<endl;
    return 0;
}

待补全

2021数学建模E题是关于自动调节系统的稳态误差问题。解题思路如下: 首先,我们需要了解自动调节系统的基本原理。自动调节系统通过测量输出信号与期望信号之间的差异,采取一系列措施来调整系统的输入信号,以实现系统输出与期望信号的一致性。其中,稳态误差是指系统在稳定状态时,输出信号与期望信号之间的残差。 针对这个问题,我们可以采用控制理论中的PID控制器进行建模和分析。PID控制器包含比例项、积分项和微分项,可以根据误差信号的大小、变化率和积分,调节系统的输入信号。 首先,我们需要建立自动调节系统的数学模型。可以考虑使用差分方程描述系统的动态行为,利用系统的输入与输出之间的关系来建立模型。然后,可以根据模型参数和系统的特性,计算出稳态误差。 接下来,我们可以通过调节PID控制器的参数来减小稳态误差。常见的方法是通过调整比例增益、积分时间常数和微分时间常数来实现。可以利用数学方法,例如反馈控制理论和优化算法,确定最佳的参数取值,以达到最小的稳态误差。 最后,我们可以通过数值模拟和仿真来验证建立的数学模型和参数调节的有效性。可以利用计算机软件,例如MATLAB,进行仿真实验,观察系统的稳态误差情况。根据仿真结果,可以进一步优化参数,以获得更好的控制效果。 总之,2021数学建模E题的解题思路是建立自动调节系统的数学模型,并利用PID控制器的参数调节来减小稳态误差。通过数值模拟和仿真实验验证模型的有效性。这样可以解决自动调节系统的稳态误差问题。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值