Kuangbin最短路专题

 

菜鸡第一篇博客 整理一下kuangbin的最短路专题(主要是ide出问题了没事干

目录

一、POJ 2387  Till the Cows Home SPFA板子

 二、POJ 2253 Frogger Dij最大边最短路

 三.POJ 1797 Heavy Transpotation Dij最小边最长路

 四、POJ 3268 Sliver Cow Party 反向建图

 五、POJ 1860 Currency Exchange SPFA判正环

 六、POJ 3259 Wormholes SPFA判负环

 七、POJ 1502 MPI Maelstrom Dij板子题

 八、Poj 3660 Cow Contest Floyd变形

 九、POJ 2240 Arbitrage Floyd变形或SPFA判正环

 十、POJ 1511 Invitation Cards 反向建图

 十一、POJ 3159 Candies 差分约束板子

 十二、POJ 2502 Subway 恶心人的板子题

 十三、POJ 1062 昂贵的聘礼 思维+枚举+Dij

 十四、POJ 1847 Tram 板子

 十五、LightOJ 1074 Extended Traffic SPFA 

十六、HDU 4725 The Shortest Path in Nya Graph 高难度建图

 十八、HDU 4370 0 or 1 思维

 十九、POJ 3169 Layout  差分约束+SPFA


一、POJ 2387  Till the Cows Home

m条边n个点 跑个最短路

SPFA板子题

#include <vector>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int N = 1005, T = 4005;
int n, t;
int dis[N], vis[N];
vector<int> to[N], edge[N];
const int inf = 1 << 29;

void spfa() {
    queue<int> q;
    for (int i = 1; i <= n; ++i) {
        dis[i] = inf;
    }
    dis[1] = 0;
    q.push(1);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = false;
        for (int i = 0; i < to[u].size(); ++i) {
            int v = to[u][i], w = edge[u][i];
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if (!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
}


int main() {
    scanf("%d%d", &t, &n);
    for (int i = 0; i < t; ++i) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        to[a].push_back(b); edge[a].push_back(c);
        to[b].push_back(a); edge[b].push_back(c);
    }
    spfa();
    printf("%d\n", dis[n]);
    return 0;
}

 二、POJ 2253 Frogger

二维平面中n个点 求从1到2,每一步权值最大,总权值最短

dij 小根堆维护最大值,跑最短路

G++要用%f输出双精度就他妈邪门

#include<iostream>
#include<queue>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<utility>
#define PII pair<double,double>
#define pii pair<double,int>
using namespace std;
const int maxn=1e6+10;
int n;
PII a[maxn];
int head[maxn],cnt,vis[maxn];
double dist[maxn];
double cal(int i,int j)
{
    double x1=a[i].first,x2=a[j].first,y1=a[i].second,y2=a[j].second;
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double dij()
{
    memset(vis,0,sizeof vis);
    for(int i=1;i<=maxn;i++) dist[i]=100000000.0;
    dist[1]=0;
    priority_queue<pii,vector<pii>,greater<pii> >heap;
    heap.push({0,1});
    while(!heap.empty())
    {
        pii now=heap.top();
        heap.pop();
        int id=now.second;
        vis[id]=1;
        for(int i=1;i<=n;i++)
        {
            double dis=cal(id,i);
            if(!vis[i]&&dist[i]>max(dis,dist[id]))
            {
                dist[i]=max(dis,dist[id]);
                heap.push({dist[i],i});
            }
        }
    }
    return dist[2];
}
int main()
{
    while(cin>>n&&n)
    {
        memset(head,-1,sizeof head);
        for(int i=1;i<=n;i++)
            cin>>a[i].first>>a[i].second;
        printf("Scenario #%d\n",++cnt);
        printf("Frog Distance = %.3f\n\n",dij());
    }
}

 三.POJ 1797 Heavy Transpotation

n个点 m条边 求每步权值最小的最长边

dij 大根堆维护最小值跑最长路 和上道题刚好反过来

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int maxn=1e6+10;
const int INF=0x3f3f3f3f;
int n,m,idx,head[maxn];
int dist[maxn],vis[maxn];
struct node
{
    int to,nex,w;
}edd[maxn];
struct cmp
{
    bool operator()(const PII x,const PII y)
    {
        return x.second<y.second;
    }
};
void addedge(int u,int v,int x)
{
    edd[idx].to=v,edd[idx].w=x,edd[idx].nex=head[u],head[u]=idx++;
}
void dij()
{
    priority_queue<PII,vector<PII>,cmp>heap;
    memset(vis,0,sizeof vis);
    memset(dist,0,sizeof dist);
    dist[1]=INF;
    heap.push({1,dist[1]});
    while(!heap.empty())
    {
        PII now=heap.top();
        heap.pop();
        int id=now.first,dis=now.second;
        if(vis[id]) continue;
        vis[id]=1;
        for(int i=head[id];i!=-1;i=edd[i].nex)
        {
            int to=edd[i].to;
            if(!vis[to]&&dist[to]<min(dist[id],edd[i].w))
            {
                dist[to]=min(dis,edd[i].w);
                heap.push({to,dist[to]});
            }
        }
    }
    return ;
}
int main()
{
    int t,cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof head);
        idx=0;
        for(int i=1;i<=m;i++)
        {
            int u,v,x;
            scanf("%d%d%d",&u,&v,&x);
            addedge(u,v,x); addedge(v,u,x);
        }
        dij();
        printf("Scenario #%d:\n%d\n\n",++cnt,dist[n]);
    }
}

 四、POJ 3268 Sliver Cow Party

n个点 m条边 求每个点i到x,再从x点回到i的最短路中最大的

从x到n个点直接跑最短路,对于每个点到x,如果每个点都跑dij肯定会t,所以选择反向建图,转变成两次从x出发的dij

#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int N = 1005;
const int INF = 1e5;
int maps[N][N];
int rmaps[N][N];	//反向图
int visit[N];
int dis[N];
int rdis[N];
int n, m, x;
void init()
{
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			rmaps[i][j] = maps[i][j] = INF;
}
void dijkstra(int d[], int maps[][N])
{
	for (int i = 1; i <= n; i++)
		visit[i] = 0;
	for (int i = 1; i <= n; i++)
	{
		d[i] = maps[x][i];
	}
	d[x] = 0;
	visit[x] = 1;
	for (int i = 1; i <= n; i++)
	{
		int t = -1;
		for (int j = 1; j <= n; j++)
		{
			if (!visit[j] && (t == -1 || d[t] > d[j]))
				t = j;
		}
		if (t == -1)return;
		visit[t] = 1;
		for (int j = 1; j <= n; j++)
		{
			if (!visit[j] && d[j] > d[t] + maps[t][j])
				d[j] = d[t] + maps[t][j];
		}
	}
}
int getAns()
{
	int ans = -1;
	for (int i = 1; i <= n; i++)
		if (i != x && dis[i] + rdis[i] > ans)ans = dis[i] + rdis[i];
	return ans;
}

int main()
{

	cin >> n >> m >> x;
	init();
	for (int i = 1; i <= m; i++)
	{
		int a, b, w;
		cin >> a >> b >> w;
		if (w < maps[a][a])	//考虑重边
			maps[a][b] = w;
		rmaps[b][a] = w;
	}
	dijkstra(dis, maps);
	dijkstra(rdis, rmaps);
	printf("%d\n", getAns());
	return 0;
}

 五、POJ 1860 Currency Exchange

n种货币,m种转换方式 给一个初始资金 问能不能使得资金变多

只要存在一种方式可以一直换一直换 那货币就能变多 因此用spfa判断正环

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=1e4+10;
const int INF=0x3f3f3f3f;
struct node
{
    int nex,to;
    double R,C;
}edd[maxn];
int n,m,st;
double money,dist[maxn];
int head[maxn],idx,cnt[maxn];
bool vis[maxn];
void addedge(int u,int v,double r,double c)
{
    edd[idx].to=v,edd[idx].R=r,edd[idx].C=c,edd[idx].nex=head[u],head[u]=idx++;
}
int spfa()
{
    memset(vis,0,sizeof vis);
    memset(dist,0,sizeof dist);
    memset(cnt,0,sizeof cnt);
    vis[st]=1,dist[st]=money,++cnt[st];
    queue<int>q;
    q.push(st);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=edd[i].nex)
        {
            int nex=edd[i].to;
            double t=(dist[now]-edd[i].C)*edd[i].R;
            if(dist[nex]<t)
            {
                dist[nex]=t;
                if(!vis[nex]) {
                    q.push(nex);
                    vis[nex]=1;
                }
                if(++cnt[nex]>n) return -1;
            }
        }
    }
    return 0;
}
int main()
{
    cin>>n>>m>>st>>money;
    memset(head,-1,sizeof head);
    for(int i=1;i<=m;i++)
    {
        int a,b; double rab,cab,rba,cba;
        cin>>a>>b>>rab>>cab>>rba>>cba;
        addedge(a,b,rab,cab);
        addedge(b,a,rba,cba);
    }
    int res=spfa();
    if(res==-1) cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}

 六、POJ 3259 Wormholes

n个点 m条无向正边 w条有向负边 问能不能从1出发 再次回到1

只要存在负环就可以走回1呗 spfa判断负环

这题大佬们的评论让我意识到了poj的评测🐔有多邪门。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
struct node
{
    int to,nex,w;
}edd[maxn];
int n,m,w;
int head[maxn],idx,cnt[maxn],dist[maxn],vis[maxn];
void addedge(int u,int v,int w)
{
    edd[idx].to=v,edd[idx].w=w,edd[idx].nex=head[u],head[u]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    memset(cnt,0,sizeof cnt);
    queue<int>q;
    dist[1]=0;
    vis[1]=1;
    cnt[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int now=q.front();
        vis[now]=0;
        q.pop();
        for(int i=head[now];i!=-1;i=edd[i].nex)
        {
            int nex=edd[i].to;
            if(dist[nex]>dist[now]+edd[i].w)
            {
                dist[nex]=dist[now]+edd[i].w;
                if(!vis[nex])
                {
                    q.push(nex);
                    vis[nex]=1;
                }
                if(++cnt[nex]>n) return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int tt;
    cin>>tt;
    while(tt--)
    {
        memset(head,-1,sizeof head);
        cin>>n>>m>>w;
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            cin>>u>>v>>w;
            addedge(u,v,w);
            addedge(v,u,w);
        }
        for(int i=1;i<=w;i++)
        {
            int u,v,w;
            cin>>u>>v>>w;
            addedge(u,v,-w);
        }
        int res=spfa();
        if(!res) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
}

 七、POJ 1502 MPI Maelstrom

喜闻乐见dij板子题

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e3+10;
int mp[maxn][maxn],dist[maxn],vis[maxn];
int n;
int getnumber(string s)
{
    int res=0;
    for(int i=0;i<s.size();i++)
        res=res*10+s[i]-'0';
    return res;
}
void dij()
{
    for(int i=1;i<=n;i++)
        dist[i]=mp[1][i];
    dist[1]=0;
    vis[1]=1;
    for(int i=2;i<=n;i++)
    {
        int t=-1;
        for(int j=2;j<=n;j++)
        {
            if(!vis[j]&&(t==-1||dist[j]<dist[t]))
                t=j;
        }
        if(t==-1) continue ;
        vis[t]=1;
        for(int j=2;j<=n;j++)
        {
            if(!vis[j]&&dist[j]>dist[t]+mp[t][j])
                dist[j]=dist[t]+mp[t][j];
        }
    }
    return ;
}
int main()
{
    cin>>n;
    memset(mp,0x3f,sizeof mp);
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            string str;
            cin>>str;
            if(str!="x") mp[j][i]=mp[i][j]=getnumber(str);
        }
    }
    dij();
    int res=-1;
    for(int i=1;i<=n;i++) res=max(res,dist[i]);
    cout<<res<<endl;
}

 八、Poj 3660 Cow Contest

n头牛 m场比赛的结果 每场比赛的结果表示为 a b 意为a战胜b(即a比b水平高)问可以确定几头牛的具体水平排行

floyd变形(就是个dp) dp[i][j]表示牛i和牛j有关,转移式为

if(dp[i][k]&&dp[k][j]) dp[i][j]=1;

如果一头牛和其他所有牛有关 他的排行就可以确定。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1e2+10;
const int INF=0x3f3f3f3f;
int f[maxn][maxn];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        f[u][v]=1;
    }
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(f[i][k]==1&&f[k][j]==1)
                    f[i][j]=1;
            }
        }
    }
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int cnt=0;
        for(int j=1;j<=n;j++)
        {
            if(f[i][j]==1) cnt++;
            if(f[j][i]==1) cnt++;
        }
        if(cnt==n-1) res++;
    }
    cout<<res<<endl;
    return 0;
}

 九、POJ 2240 Arbitrage

和第五题差不多的意思 跑个spfa判断正环即可

但也可以用floyd变形去做

dp[i][j]表示i能换为多少倍的j 转移式:dp[i][j]=dp[i][k]*dp[k][j];

最后看dp[i][i]有没有大于1的即可

#include<iostream>
#include<cmath>
#include<cstdio>
#define db double
using namespace std;
const int maxn=1e3+10;
char c[maxn][maxn];
double mp[maxn][maxn];
int n,m;
int change(char tmp[])
{
    for(int i=1;i<=m;i++)
        if(strcmp(tmp,c[i])==0)
            return i;
    return 0;
}
bool floyd()
{
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                mp[i][j] = max(mp[i][j], mp[i][k] * mp[k][j]);
    for(int i=1;i<=n;i++)
        if(mp[i][i]>1) {
            return 1;
        }
    return 0;
}
int main()
{
    int cnt=0;
    while(cin>>n&&n!=0)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%s",c[i]);
        }
        cin>>m;
        memset(mp,0,sizeof(mp));
        for(int i=1;i<=n;i++)
            mp[i][i]=1;
        for(int i=1;i<=m;i++)
        {
            double rate;
            char tmp1[maxn],tmp2[maxn];
            scanf("%s%lf%s",tmp1,&rate,tmp2);
            mp[change(tmp1)][change(tmp2)]=rate;
        }
        if(floyd()) printf("Case %d: Yes\n",++cnt);
        else printf("Case %d: No\n",++cnt);
    }
    return 0;
}

 十、POJ 1511 Invitation Cards

和第四题类似 反向建图跑最短路

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e6+10;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m;
ll w[maxn],dist1[maxn],dist2[maxn];
struct node
{
    int to,nex;
    ll w;
}save[maxn*2];
int head[maxn],rhead[maxn],cnt;
bool vis[maxn],choice;
void init()
{
    cnt=0;
    memset(dist1,0x3f,sizeof dist1);
    memset(dist2,0x3f,sizeof dist2);
    memset(save,0,sizeof save);
    memset(head,-1,sizeof head);
    memset(rhead,-1,sizeof rhead);
}
void addedge(int head[],int u,int v,ll x)
{
    save[cnt].to=v,save[cnt].w=x,save[cnt].nex=head[u],head[u]=cnt++;
}
struct cmp {
    bool operator()(int a, int b)
    {
        if(choice==1)
            return dist1[a] > dist1[b];    //这比较函数,涨见识,可以用到全局变量
        return dist2[a] > dist2[b];
    }
};
void dij(ll dist[],int head[])
{
    memset(vis,0,sizeof vis);
    priority_queue<int,vector<int>,cmp>heap;
    dist[1]=0;
    heap.push(1);
    while(!heap.empty())
    {
        int now=heap.top();
        heap.pop();
        if(vis[now]) continue;
        vis[now]=1;
        for(int i=head[now];i!=-1;i=save[i].nex)
        {
            int t=save[i].to;
            if(!vis[t]&&dist[t]>dist[now]+save[i].w)
            {
                dist[t]=dist[now]+save[i].w;
                heap.push(t);
            }
        }
    }
    return ;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u,v; ll x;
            scanf("%d%d%lld",&u,&v,&x);
            addedge(head,u,v,x);
            addedge(rhead,v,u,x);
        }
        choice=1;
        dij(dist1,head);
        choice=0;
        dij(dist2,rhead);
        ll res=0;
        for(int i=1;i<=n;i++)
        {
            res+=dist1[i]+dist2[i];
        }
        //res+=(dist1[i]+dist2[i]);
        printf("%lld\n",res);
    }
    return 0;
}

 十一、POJ 3159 Candies

n个小孩 m条信息 每条表示为a b c 意为a的糖果数+c>=b的糖果数

差分约束板子题,建b->a权值为c的单向边

拜托 不用快读就t真的很蠢诶

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#include<utility>
#define ll long long
using namespace std;
const int maxn=150000+10;
typedef pair<int,int>PII;
template <typename T>
    void read(T &x){
        x=0;
        char ch=getchar();
        ll f=1;
        while(!isdigit(ch)){
            if(ch=='-')
                f*=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            x=x*10+ch-48;
            ch=getchar();
        }
        x*=f;
    }
struct node
{
    int nex,to,w;
}edd[maxn*2];
int n,m;
int dist[maxn*2],vis[maxn*2];
int head[maxn*2],idx;
void addedge(int u,int v,int w)
{
    edd[idx].to=v,edd[idx].w=w,edd[idx].nex=head[u],head[u]=idx++;
}
void dij()
{
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    priority_queue<PII,vector<PII>,greater<PII> >heap;
    dist[1]=0;
    heap.push({dist[1],1});
    while(!heap.empty())
    {
        PII now=heap.top();
        heap.pop();
        int id=now.second,dis=now.first;
        vis[id]=1;
        for(int i=head[id];i!=-1;i=edd[i].nex)
        {
            int nex=edd[i].to;
            if(dist[nex]>dis+edd[i].w&&!vis[nex])
            {
                dist[nex]=dis+edd[i].w;
                heap.push({dist[nex],nex});
            }
        }
    }
    return ;
}
int main()
{
    read(n),read(m);
    memset(head,-1,sizeof head);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        read(u),read(v),read(w);
        addedge(u,v,w);
    }
    dij();
    printf("%d\n",dist[n]);
    return 0;
}

 十二、POJ 2502 Subway

二维平面 给定起点,终点,有多条地铁线,步行速度10km/h 地铁速度40km/h 问起点到终点的最短路

我愿称之为建图最恶心的题

首先给的速度是km/h 最后问的是多少分钟 涉及进制转换

然后是需要建的边:同一条地铁线相邻站点的速度为40的边(仅该条件下可以坐车)和速度为10的边,起点到所有站台的速度为10的边,所有站台到终点的边

最后结果四舍五入,也不给说 还得自己试怎么保留小数 真的吐了

处理完这一堆乱七八糟的东西就跑个最短路板子就行

#include<iostream>
#include<queue>
#include<cmath>
#define debug(x) cerr<<#x<<"->"<<x<<endl;
using namespace std;
typedef pair<int,int> PII;
double x_st,y_st,x_ed,y_ed;
const int maxn=5e4+10;
const int INF=0x3f3f3f3f;
PII node[1010];
struct eddedege
{
    int nex,to;
    double w;
}edd[maxn];
int head[1010],vis[1010],cnt,id=1;
double dist[1010];
void add(int u,int v,double w)
{
    edd[cnt].to=v,edd[cnt].w=w,edd[cnt].nex=head[u],head[u]=cnt++;
}
double getdist(int x1,int y1,int x2,int y2,int v)
{
    double x=x2-x1,y=y2-y1;
    return sqrt(x*x+y*y)/v/1000*60;
}
bool cmp(double a,double b)
{
    if(a-b>1e-8) return 1;
    return 0;
}
void spfa()
{
    for(int i=1;i<id;i++) dist[i]=INF;
    dist[0]=0,dist[201]=INF;
    queue<int>q;
    q.push(0);
    vis[0]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=edd[i].nex)
        {
            int next=edd[i].to;
            if(cmp(dist[next],dist[now]+edd[i].w))
            {
                dist[next]=edd[i].w+dist[now];
                if(!vis[next])
                {
                    q.push(next);
                    vis[next]=1;
                }
            }
        }
    }
    return ;
}
int main()
{
    cin>>x_st>>y_st>>x_ed>>y_ed;
    memset(head,-1,sizeof head);
    while(cin>>node[id].first>>node[id].second)
    {
        int x=node[id].first,y=node[id].second;
        add(0,id,getdist(x,y,x_st,y_st,10));
        add(id,201,getdist(x,y,x_ed,y_ed,10));
        id++;
        while(cin>>node[id].first>>node[id].second&&node[id].first!=-1)
        {
            int x=node[id].first,y=node[id].second;
            int lx=node[id-1].first,ly=node[id-1].second;
            add(0,id,getdist(x,y,x_st,y_st,10));
            add(id,201,getdist(x,y,x_ed,y_ed,10));
            add(id,id-1,getdist(x,y,lx,ly,40));
            add(id-1,id,getdist(x,y,lx,ly,40));
            id++;
        }
    }
    for(int i=1;i<id;i++)
    {
        for(int j=i+1;j<id;j++)
        {
            add(i,j,getdist(node[i].first,node[i].second,node[j].first,node[j].second,10));
            add(j,i,getdist(node[i].first,node[i].second,node[j].first,node[j].second,10));
        }
    }
    spfa();
    cout<<(int)(dist[201]+0.5)<<endl;
    return 0;
}

 十三、POJ 1062 昂贵的聘礼

输入第一行是两个整数M,N(1 <= N <= 100),依次表示地位等级差距限制和物品的总数。接下来按照编号从小到大依次给出了N个物品的描述。每个物品的描述开头是三个非负整数P、L、X(X < N),依次表示该物品的价格、主人的地位等级和替代品总数。接下来X行每行包括两个整数T和V,分别表示替代品的编号和"优惠价格"。(终于有中文题给我ctrlCctrlV了)

枚举地位区间内的点,跑dij

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=110;
const int INF=0x3f3f3f3f;
int val[maxn],lev[maxn],mp[maxn][maxn];
int dist[maxn],vis[maxn],o[maxn];
int m,n;
struct node
{
    int id,dis;
    node (int a,int b){
        id=a,dis=b;
    }
    bool operator<(const node& a)const {
            return a.dis < dis;
    }
};
int dij()
{
    memset(vis,0,sizeof vis);
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    priority_queue<node>heap;
    heap.push(node(1,dist[1]));
    while(!heap.empty())
    {
        node now=heap.top();
        heap.pop();
        int id=now.id,dis=now.dis;
        //cout<<id<<" "<<dis<<endl;
        vis[id]=1;
        for(int i=1;i<=n;i++)
        {
            if(dist[i]>dist[id]+mp[id][i]&&!vis[i]&&o[i])
            {
                dist[i]=dist[id]+mp[id][i];
                heap.push(node(i,dist[i]));
            }
        }
    }
    int ans=INF;
    for(int i=1;i<=n;i++)
    {
        if(ans>dist[i]+val[i])
            ans=dist[i]+val[i];
    }
    return ans;
}
int main()
{
    memset(mp,0x3f,sizeof mp);
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
        int t;
        cin>>val[i]>>lev[i]>>t;
        for(int j=1;j<=t;j++)
        {
            int y,z;
            cin>>y>>z;
            mp[i][y]=z;
        }
    }
    int ans=INF;
    for(int i=0;i<=m;i++)
    {
        memset(o,0,sizeof o);
        for(int j=1;j<=n;j++)
        {
            if(lev[1]-m+i<=lev[j]&&lev[1]+i>=lev[j])
                o[j]=1;
        }
        ans=min(ans,dij());
    }
    cout<<ans<<endl;
}

 十四、POJ 1847 Tram

给n个结点,起点和终点,每个节点有ki条路 默认走第一条路,按一次开关可以改变走的路,问从起点到终点最少按的开关数

将初始道路的权值设为0,其他的设为1,跑最短路即可

#include<iostream>
#include<cstring>
#include<queue>
const int maxn=1e3+10;
const int INF=0x3f3f3f3f;
using namespace std;
int n,st,ed;
struct node
{
    int nex,to,w;
}edd[maxn];
int head[maxn],cnt;
int dist[maxn],vis[maxn];
void addedge(int u,int v,int x)
{
    edd[cnt].to=v,edd[cnt].w=x,edd[cnt].nex=head[u],head[u]=cnt++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    queue<int>q;
    dist[st]=0;
    q.push(st);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=edd[i].nex)
        {
            int t=edd[i].to;
            if(dist[t]>dist[now]+edd[i].w)
            {
                dist[t]=dist[now]+edd[i].w;
                if(!vis[t])
                {
                    vis[t]=1;
                    q.push(t);
                }
            }
        }
    }
    if(dist[ed]!=INF) return dist[ed];
    else return -1;
}
int main()
{
    memset(head,-1,sizeof head);
    cin>>n>>st>>ed;
    for(int i=1;i<=n;i++)
    {
        int t;
        cin>>t;
        for(int j=1;j<=t;j++)
        {
            int v;
            cin>>v;
            if(j==1) addedge(i,v,0);
            else addedge(i,v,1);
        }
    }
    cout<<spfa()<<endl;
}

 十五、LightOJ 1074 Extended Traffic

n个节点 没个节点有权值wi,a到b的边权为(wa-wb)^3,q次查询,每次给终点ed,询问1~ed的最短路,如果无法达到或权值<3输出?

立方存在负边,spfa裸题

 

​
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=1e6+10;
const int INF=0x3f3f3f3f;
int n,m,d[maxn],dist[maxn],cnt[maxn];
bool vis[maxn],fu[maxn];
struct node
{
    int nex,to,w;
}edd[maxn];
int head[maxn],idx,kase;
void addedge(int u,int v,int w)
{
    edd[idx].to=v,edd[idx].w=w,edd[idx].nex=head[u],head[u]=idx++;
}
void dfs(int n)
{
    fu[n]=1;
    for(int i=head[n];i!=-1;i=edd[i].nex)
        if(!fu[edd[i].to])
            dfs(edd[i].to);
    return ;
}
void spfa()
{
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    memset(fu,0,sizeof fu);
    memset(cnt,0,sizeof cnt);
    dist[1]=0; vis[1]=1; ++cnt[1];
    queue<int>q;
    q.push(1);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=edd[i].nex)
        {
            int nex=edd[i].to;
            if(!fu[nex]&&dist[nex]>edd[i].w+dist[now])
            {
                dist[nex]=edd[i].w+dist[now];
                if(!vis[nex])
                {
                    vis[nex]=1;
                    q.push(nex);
                    if(++cnt[nex]>n) dfs(nex);//找到负环后递归打上标记
                }
            }
        }
    }
}
void solve()
{
    memset(head,-1,sizeof head);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>d[i];
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        int w=(d[v]-d[u])*(d[v]-d[u])*(d[v]-d[u]);
        addedge(u,v,w);
    }
    spfa();
    int q;
    cin>>q;
    printf("Case %d:\n",++kase);
    while(q--)
    {
        int x;
        cin>>x;
        if(dist[x]<3||dist[x]==INF||fu[x]) cout<<"?"<<endl;//如果点在负环中输出?
        else cout<<dist[x]<<endl;
    }
    return;
}
int main()
{
    int tt;
    cin>>tt;
    while(tt--) solve();
    return 0;
}

十六、HDU 4725 The Shortest Path in Nya Graph

n个点 m条边,但每个点分层,相邻的两层之间任意点存在边权为c的无向边,问1~N的最短路。

分层导致离线处理变得麻烦,而且如果i,i+2层存在点,i+1层不存在,i和i+2就无法连接

很妙的建图:对于每一层 开一个超级源点,建立该层的超级源点到该层每个点的边权为0的有向边,以及该层每个点到上一层和下一层超级原点的有向边(这不比十二题那个恶心人的建图有含金量)(但是能不能不要卡快读)

建图之后跑最短路就可以

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>PII;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
int n,m,c;
template <typename T>
inline void read(T &x) {
	x = 0;
	int f = 1;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = x * 10 + ch - '0', ch = getchar();
	}
	x *= f;
}

template <typename T>
void write(T x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}
struct node
{
    int nex,to,w;
}edge[10*maxn];//这里的最大值由于多加了很多边,开大点防止越界
int head[10*maxn],idx,cnt;
int dist[2*maxn],vis[2*maxn];
void addedge(int u,int v,int w)
{
    edge[idx].to=v,edge[idx].w=w,edge[idx].nex=head[u],head[u]=idx++;
}
int dij()
{
    memset(dist,INF,sizeof dist);
    memset(vis,0,sizeof vis);
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    dist[1]=0;
    heap.push({dist[1],1});
    while(!heap.empty())
    {
        PII now=heap.top();
        heap.pop();
        int id=now.second,dis=now.first;
        vis[id]=1;
        for(int i=head[id];i!=-1;i=edge[i].nex)
        {
            int nex=edge[i].to;
            if(!vis[nex]&&dist[nex]>edge[i].w+dis)
            {
                dist[nex]=edge[i].w+dis;
                heap.push({dist[nex],nex});
            }
        }
    }
    if(dist[n]==INF) return -1;
    else return dist[n];
}
void solve()
{
    idx=0;
    memset(head,-1,sizeof head);
    read(n),read(m),read(c);
    for(int i=1;i<=n;i++)
    {
        int l; read(l);
        addedge(l+n,i,0);
        if(l>1) addedge(i,l+n-1,c);
        if(l<n) addedge(i,l+n+1,c);
    }
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        read(u),read(v),read(w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    printf("Case #%d: ",++cnt);
    write(dij());
    printf("\n");
}
int main()
{
    int tt;
    read(tt);
    while(tt--) solve();
    return 0;
}

 

 十八、HDU 4370 0 or 1

给你一个n*m的矩阵C,让你确定一个01构成的矩阵K,使得sum(Cij*Kij)最小,输出这个最小值。

对于矩阵K,满足下列条件

1.X12+X13+...X1n=1 
2.X1n+X2n+...Xn-1n=1 
3.对于所有的 i (1<i<n), 满足∑Xki (1<=k<=n)=∑Xij (1<=j<=n). 

很有意思的思维题

按题目要求 第一行和最后一列一定是有Cij要被计算的,可以建立一个超级源点和一个超级汇点,然后手摸一下第三个条件 会发现K矩阵实际上就是一个邻接矩阵,Cij为对应的权值,这样三个条件就分别转换成了:

1.起点出度为1

2.终点入度为1

3.每个点的入度和出度相等

很明显可以看出这是个最短路,但还有一种情况,起点自环+终点自环,求包括起点的自环先把起点联通的点初始化为0后入栈即可。最后两种情况求最小值。

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=310;
const int INF=0x3f3f3f3f;
int mp[maxn][maxn];
int dist[maxn],vis[maxn];
int n;
void spfa(int st)
{
    queue<int>q;
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++)
    {
        if(i==st)
        {
            dist[i]=INF;
            vis[i]=0;
        }
        else
        {
            dist[i]=mp[st][i];
            vis[i]=1;
            q.push(i);//与1号点联通的点先入栈
        }
    }
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=1;i<=n;i++)
        {
            if(dist[i]>dist[now]+mp[now][i])
            {
                dist[i]=dist[now]+mp[now][i];
                if(!vis[i])
                {
                    vis[i]=1;
                    q.push(i);
                }
            }
        }
    }
    return ;
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&mp[i][j]);
        spfa(1);
        int c1=dist[1];//1号点自环
        int ans=dist[n];//最短路
        spfa(n);
        int c2=dist[n];//n号点自环
        ans=min(ans,c1+c2);
        printf("%d\n",ans);
    }
}
//?????

 十九、POJ 3169 Layout

有N个小学生按编号顺序排队。有ML个小学生之间关系比较好,所以希望彼此之间不超过一定距离,有MD个小学生关系比较差,所以希望彼此之间至少满足某个距离。有可能有多个小学生在同一个位置上。在满足这些条件的排列方法中,求1号学生和N号学生的最大距离,如果不存在可能的排列方法输出-1,如果距离可以无限大就输出-2.

一眼差分约束 考虑怎么建图

对于关系好的 有du-dv<=w,建立v到u权值为w的有向边。对于关系不好的,有du-dv>=w,转换为<= ,建议u到v权值为-w的有向边,而他要求按编号排序,也就是说要求si<si+1 那就建立一条i+1到i权值为0的边即可。

那对应-1和-2分别是什么情况:

首先说-1,没有任何可能的排列方法,显而易见:存在负环。然后是-2,距离可以无限大也就是说两个小崽子之间没有任何关系,也就是两个人不存在最短路。用spfa判负环以及求最短路。

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
struct node
{
    int to,nex,w;
}edge[maxn];
int n,m,l;
int dist[maxn],vis[maxn],cnt[maxn];
int head[maxn],idx;
void addedge(int u,int v,int w)
{
    edge[idx].to=v,edge[idx].w=w,edge[idx].nex=head[u],head[u]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    memset(vis,0,sizeof vis);
    dist[1]=0;
    vis[1]=0;
    cnt[1]=1;
    queue<int>q;
    q.push(1);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=edge[i].nex)
        {
            int nex=edge[i].to;
            if(dist[nex]>dist[now]+edge[i].w)
            {
                dist[nex]=dist[now]+edge[i].w;
                if(!vis[nex])
                {
                    vis[nex]=1;
                    q.push(nex);
                }
                if(++cnt[nex]>n) return -1;
            }
        }
    }
    if(dist[n]>INF/2) return -2;
    else return dist[n];
}
int main()
{
    cin>>n>>m>>l;
    memset(head,-1,sizeof head);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        addedge(u,v,w);
    }
    for(int i=1;i<=l;i++)
    {
        int u,v,w;
        cin>>u>>v>>w;
        addedge(v,u,-w);
    }
    for(int i=1;i<n;i++) addedge(i+1,i,0);
    int res=spfa();
    cout<<res<<endl;
}

少的那个17题据说是和网络流有关,等菜鸡学到了就补(下次一定

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值