https://cn.vjudge.net/problem/POJ-1062
枚举区间,以国王的等级为起点枚举,如图(有点丑)
然后第一次枚举的时候1,2,3,4等级的可以走,第二次,2,3,4,5,第三次3,4,5,6,然后跑最短路
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=300;
int dis[maxn];
bool vis[maxn],v[maxn];
int ji[maxn],m,n,w[maxn];
bool can[maxn];
const long long inf=0x3f3f3f3f;
struct node
{
int to,cap;
node(){}
node(int to,int cap):to(to),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
};
vector<node>g[maxn];
void add(int u,int v,int cap)
{
g[u].push_back(node(v,cap));
// g[v].push_back(node(u,cap));
}
int dijkstra(int s)
{ int ans=0x3f3f3f3f;
memset(vis,false,sizeof(vis));
priority_queue<node>q;
q.push(node(s,0));
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[s]=0;
while(!q.empty())
{
node s=q.top();q.pop();
//if(dis[s.to]>s.cap)
if(vis[s.to]||!can[s.to])continue;
vis[s.to]=true;
//ans+=s.cap;
for(int i=0;i<g[s.to].size();i++)
{
if(!vis[g[s.to][i].to]&&dis[g[s.to][i].to]>dis[s.to]+g[s.to][i].cap)
{
dis[g[s.to][i].to]=dis[s.to]+g[s.to][i].cap;
q.push(g[s.to][i]);
}
}
}
for(int i=1;i<=n;i++)
{ if(can[i])
ans=min(ans,w[i]+dis[i]);
}
return ans;
}
int main()
{ int ans=0x3f3f3f3f;
scanf("%d %d",&m,&n);
for(int i=1;i<=n;i++)
{ //if(i!=1)add(0,i,0);
int cos,le,e;
scanf("%d %d %d",&cos,&le,&e);
ji[i]=le;//level表示等级,中英混用
w[i]=cos;//cost
for(int j=0;j<e;j++)
{
int v,co;
scanf("%d %d",&v,&co);
add(i,v,co);
}
}
for(int i=0;i<=m;i++)//此处枚举区间
{ memset(can,false,sizeof(can));//这个一定要有
//can[0]=true;
for(int j=1;j<=n;j++)
{
if(ji[j]>=ji[1]-m+i&&ji[j]<=ji[1]+i)
{
can[j]=true;
}
}
ans=min(ans,dijkstra(1));
}
cout<<ans<<endl;
}
https://cn.vjudge.net/problem/POJ-2253
题意:找从编号为1-2之间的所有路径中,每条路径上最大值最小化。这是一个完全图。
n=200。不建议用前向星和链表存图(边很多),直接开个200*200数组就解决了。
题解:有一些动态规划的思想g[i][j]表示从i-j的最大值的最小化,然后遍历他的所有编,在更新下一个状态。
Floyd解法
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
double g[3000][300];
int n,cnt;
struct Point
{
int x,y;
Point(){}
Point(int x,int y):x(x),y(y){}
}p[maxn];
double dis(Point a,Point b)
{
return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
void floyd()
{
for(int k=0;k<n;k++)//这个思想就是,从K点转移
//从i-j的最小值。所以先遍历k
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[j][i]=g[i][j]=min(g[i][j],max(g[i][k],g[k][j]));
}
int main()
{
while(scanf("%d",&n)!=EOF&&n){
memset(g,inf,sizeof(g));
for(int i=0;i<n;i++)
{
scanf("%d %d",&p[i].x,&p[i].y);
}
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
g[i][j]=dis(p[i],p[j]);//完全图建边
}
}
floyd();
//printf("Scenario #%d\nFrog Distance = %.3lf\n\n",q++,map[1][2]);
printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++cnt,g[0][1]);
}
}
dijkstra法
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
double g[300][300];
int n,cnt;
bool vis[300];
double dist[300];
struct Point
{
int x,y;
Point(){}
Point(int x,int y):x(x),y(y){}
}p[maxn];
double dis(Point a,Point b)
{
return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}
void dij(int s)//其实想法是差不多的,这个也不算dijkstra只是一种思想
{
memset(vis,false,sizeof(vis));
for(int i=0;i<300;i++)dist[i]=0x3f3f3f3f;
dist[0]=0;
for(int i=0;i<n;i++)
{ int minn=0x3f3f3f3f,k;
for(int j=0;j<n;j++)//这个循环每次寻找最短的那条边
{
if(!vis[j]&&dist[j]<minn)
{
minn=dist[j];
k=j;
}
}
vis[k]=true;//这就是最短的那条边
for(int j=0;j<n;j++) 更新状态
dist[j]=min(dist[j],max(dist[k],g[k][j]));
}
}
int main()
{
while(scanf("%d",&n)!=EOF&&n){
//memset(g,inf,sizeof(g));
for(int i=0;i<n;i++)
{
scanf("%d %d",&p[i].x,&p[i].y);
}
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
g[j][i]=g[i][j]=dis(p[i],p[j]);
}
}
dij(0);
//printf("Scenario #%d\nFrog Distance = %.3lf\n\n",q++,map[1][2]);
printf("Scenario #%d\nFrog Distance = %.3lf\n\n",++cnt,dist[1]);
}
}
建边的时候注意双向边!!!
https://cn.vjudge.net/problem/POJ-1860
典型的BF问题,题目的意思是判是否有正环,那就判断能否已知松弛,并且是反着来松弛
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=300;
int cnt;
double dis[maxn];
int N,M,S;
double V;
struct node
{
int u,v;
double re,ra;
}g[maxn];
void add(int u,int v,double re,double ra)
{
g[cnt].u=u;
g[cnt].v=v;
g[cnt].ra=ra;
g[cnt].re=re;
cnt++;
}
bool BF()
{
for(int i=0;i<N;i++)dis[i]=0;
dis[S]=V;
for(int j=1;j<N;j++)//BF思想,进行N-1次的松弛操作
{ bool ok=false;
for(int i=0;i<cnt;i++)//对源点,经过第j条边时的松弛
//也就是不断的更新。
{
if(dis[g[i].v]<(dis[g[i].u]-g[i].ra)*g[i].re)
dis[g[i].v]=(dis[g[i].u]-g[i].ra)*g[i].re,ok=true;
}
if(!ok)return 0;//若没松弛完便无法松弛了,那么也就不存在正环。
}
for(int j=0;j<cnt;j++)//判断是否存在正环
{
if(dis[g[j].v]<(dis[g[j].u]-g[j].ra)*g[j].re)
return 1;
}
return 0;
}
int main()
{
int u,v;
double re,ra,ve,va;
cin>>N>>M>>S>>V;
for(int i=0;i<M;i++)
{
cin>>u>>v>>re>>ra>>ve>>va;
add(u,v,re,ra);
add(v,u,ve,va);
}
if(BF())cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
BF判断负环,跟上面的操作类似,就是反过来
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=6000;
int total;
int n,m,w;
int dis[maxn];
struct node
{
int u,v,cap;
node(){}
node(int u,int v,int cap):u(u),v(v),cap(cap){}
}g[maxn];
void add(int u,int v,int cap)
{
g[total++]=node(u,v,cap);
}
bool BF(int s)
{
for(int i=0;i<n;i++)
dis[i]=0x3f3f3f3f;
dis[s]=0;
for(int i=0;i<n;i++)
{ bool ok=false;
for(int j=0;j<total;j++)//松弛
{
if(dis[g[j].v]>dis[g[j].u]+g[j].cap)
dis[g[j].v]=dis[g[j].u]+g[j].cap,ok=true;
}
if(!ok)return false;//已经不能松弛了,说明没有跑完N-1就已经到达最好的值,所以不存在环
}
for(int i=0;i<total;i++)//跑完后,判断是否有负环
if(dis[g[i].v]>dis[g[i].u]+g[i].cap)return true;
return false;
}
int main()
{
int t;
cin>>t;
while(t--)
{ total=0;
cin>>n>>m>>w;
for(int i=0;i<maxn;i++)
g[i].u=g[i].v=g[i].cap=0;
int u,v,ca;
for(int i=0;i<m;i++)
{
cin>>u>>v>>ca;
add(u,v,ca);
add(v,u,ca);
}
for(int i=0;i<w;i++)
{
cin>>u>>v>>ca;
add(u,v,-ca);
}
bool ok=false;
if(BF(1))
printf("YES\n");
else printf("NO\n");
}
}
https://cn.vjudge.net/problem/POJ-1502
一道最短路的裸题吧···比较难的地方可能就是只给了一个三角矩阵了,自己还原后找规律就知道了;
样例的原矩阵是这个
0 50 30 100 10
50 0 5 20 x
30 5 0 50 x
100 20 50 0 10
10 x x 10 0
用floyd跑一编,然后g[0][i]的最大值就是答案。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=200;
int n;
#define inf 0x7ffffff
int g[maxn][maxn];
int mp[maxn][maxn];
int get(string s)
{
int ans=0;
for(int i=0;i<s.size();i++)
{
ans=ans*10+(s[i]-'0');
}
return ans;
}
void floyd()
{
for(int k=0;k<n;k++)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
}
}
}
int main()
{
memset(g,0x3f3f3f3f,sizeof(g));
cin>>n;
for(int i=0;i<n;i++)
g[i][i]=0;
for(int i=0;i<n-1;i++)
{
for(int j=0;j<i+1;j++)
{
string s;
cin>>s;
if(s=="x")continue;
else g[i+1][j]=g[j][i+1]=get(s);//记得双向边
}
}
floyd();
int res=0;
for(int i=0;i<n-1;i++)
{
res=max(g[0][i],res);
}
cout<<res<<endl;
}
https://cn.vjudge.net/problem/POJ-3660
思路:首先考虑怎么样确定关系?即这个点与别的n-1个点都有联系,那么就floyd跑一遍最短路,然后每个点特判就好了。
还有一个方法,就是记录入度与出度只要 入度+出度=n-1就可以了。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
const int maxn=300;
int g[maxn][maxn],n,m;
void floyd()
{
for(int k=0;k<n;k++)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
}
}
}
int main()
{
cin>>n>>m;
memset(g,0x3f3f3f3f,sizeof(g));
for(int i=0;i<n;i++)
g[i][i]=0;
for(int i=0;i<m;i++)
{
int u,v;
cin>>u>>v;
g[--u][--v]=1;
}
floyd();
int ans=0;
for(int i=0;i<n;i++)
{
bool ok=true;
for(int j=0;j<n;j++)
{ //cout<<g[i][j]<<endl;
if(g[i][j]>=50000&&g[j][i]>40000)
{
ok=false;
break;
}
}
if(ok)ans++;
}
cout<<ans<<endl;
}
https://cn.vjudge.net/problem/POJ-3159
这题很经典的题型了,先正着跑一边dij的堆优化然后反着跑一边,加起来就是答案
用vector建图会被卡常超时,这里用邻接表,也挺好用的。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
#define maxn 1000100
#define inf 1LL<<60
struct node
{
int to,cap,next;
node(){}
node(int to,int cap):to(to),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
}g1[maxn*10],g2[maxn*10];
int head1[maxn],head2[maxn];
long long dis[maxn];
bool vis[maxn];
int n,m,cnt;
void add(node *mp,int *head,int u,int v,int cap)
{
mp[cnt].to=v;
mp[cnt].cap=cap;
mp[cnt].next=head[u];
head[u]=cnt;
}
long long dijkstra(node *g,int *head,int s)
{ long long res=0;
memset(vis,false,sizeof(vis));
for(int i=0;i<=n;i++)dis[i]=inf;
dis[s]=0;
priority_queue<node>q;
q.push(node(1,0));
while(!q.empty())
{
node s=q.top();q.pop();
if(vis[s.to])continue;
vis[s.to]=true;
for(int i=head[s.to];i!=-1;i=g[i].next)
{ //cout<<i<<endl;
if(!vis[g[i].to]&&dis[g[i].to]>dis[s.to]+g[i].cap)
{
dis[g[i].to]=dis[s.to]+g[i].cap;
q.push(g[i]);
}
}
}
for(int i=1;i<=n;i++)
{
res+=dis[i];
}
return res;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&m);
memset(head1,-1,sizeof(head1));
memset(head2,-1,sizeof(head2));
for(int i=0;i<m;i++)
{
int u,v,ca;
scanf("%d %d %d",&u,&v,&ca);
// cout<<u<<" "<<v<<" "<<ca<<endl;
add(g1,head1,u,v,ca);//存正图
add(g2,head2,v,u,ca);//存反图
cnt++;
//g1[v].push_back(node(u,ca));
//cout<<g[u].size()<<endl;
}
//for(int i=0;i<n;i++)cout<<g[i].size()<<endl;
long long ans=dijkstra(g1,head1,1)+dijkstra(g2,head2,1);
printf("%lld\n",ans);
}
}
https://cn.vjudge.net/problem/POJ-1511
两题放在一起因为题型类似,这题以1为起点跑一遍最短路,再以n为起点跑一遍,取最小的最是答案了
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
#define maxn 1000100
#define inf 1LL<<60
struct node
{
int to,cap,next;
node(){}
node(int to,int cap):to(to),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
}g1[maxn*10],g2[maxn*10];
int head1[maxn],head2[maxn];
long long dis[maxn];
bool vis[maxn];
int n,m,cnt;
void add(node *mp,int *head,int u,int v,int cap)
{
mp[cnt].to=v;
mp[cnt].cap=cap;
mp[cnt].next=head[u];
head[u]=cnt;
}
void dijkstra(node *g,int *head,int s)
{
memset(vis,false,sizeof(vis));
for(int i=0;i<=n;i++)dis[i]=inf;
dis[s]=0;
priority_queue<node>q;
q.push(node(1,0));
while(!q.empty())
{
node s=q.top();q.pop();
if(vis[s.to])continue;
vis[s.to]=true;
for(int i=head[s.to];i!=-1;i=g[i].next)
{ //cout<<i<<endl;
if(!vis[g[i].to]&&dis[g[i].to]>dis[s.to]+g[i].cap)
{
dis[g[i].to]=dis[s.to]+g[i].cap;
q.push(g[i]);
}
}
}
}
int main()
{
scanf("%d %d",&n,&m);
memset(head1,-1,sizeof(head1));
for(int i=0;i<m;i++)
{
int u,v,ca;
scanf("%d %d %d",&u,&v,&ca);
add(g1,head1,u,v,ca);
cnt++;
}
long long int ans=inf;
dijkstra(g1,head1,1);
ans=min(dis[n],ans);
dijkstra(g1,head1,n);
ans=min(dis[1],ans);
printf("%lld\n",ans);
}
https://cn.vjudge.net/problem/POJ-1062
题解:枚举区间跑dijkstra,一开始想用floyd然后逐项判断的,发现行不通,无法根据等级判断。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=300;
int dis[maxn];
bool vis[maxn],v[maxn];
int ji[maxn],m,n,w[maxn];
bool can[maxn];
const long long inf=0x3f3f3f3f;
struct node
{
int to,cap;
node(){}
node(int to,int cap):to(to),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
};
vector<node>g[maxn];
void add(int u,int v,int cap)
{
g[u].push_back(node(v,cap));
// g[v].push_back(node(u,cap));
}
int dijkstra(int s)
{ int ans=0x3f3f3f3f;
memset(vis,false,sizeof(vis));
priority_queue<node>q;
q.push(node(s,0));
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[s]=0;
while(!q.empty())
{
node s=q.top();q.pop();
//if(dis[s.to]>s.cap)
if(vis[s.to]||!can[s.to])continue;
vis[s.to]=true;
//ans+=s.cap;
for(int i=0;i<g[s.to].size();i++)
{
if(!vis[g[s.to][i].to]&&dis[g[s.to][i].to]>dis[s.to]+g[s.to][i].cap)
{
dis[g[s.to][i].to]=dis[s.to]+g[s.to][i].cap;
q.push(g[s.to][i]);
}
}
}
for(int i=1;i<=n;i++)
{ if(can[i])
ans=min(ans,w[i]+dis[i]);
}
return ans;
}
int main()
{ int ans=0x3f3f3f3f;
scanf("%d %d",&m,&n);
for(int i=1;i<=n;i++)
{ //if(i!=1)add(0,i,0);
int cos,le,e;
scanf("%d %d %d",&cos,&le,&e);
ji[i]=le;
w[i]=cos;
for(int j=0;j<e;j++)
{
int v,co;
scanf("%d %d",&v,&co);
add(i,v,co);
}
}
for(int i=0;i<=m;i++)//枚举区间
{ memset(can,false,sizeof(can));
//can[0]=true;
for(int j=1;j<=n;j++)
{
if(ji[j]>=ji[1]-m+i&&ji[j]<=ji[1]+i)
{
can[j]=true;
}
}
ans=min(ans,dijkstra(1));
}
cout<<ans<<endl;
}
https://cn.vjudge.net/problem/POJ-2502
这题主要考建图把,一开始建崩了,还是不知道怎么错的,后来修改了。唉。。。
每个地铁站以及学校和家作为点,边值就是两个点通过任意方式到达的最短时间,然后跑一遍最短路,可能会有重边,注意四舍五入的判断
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define inf 0x3f3f3f3f
int k;
struct Point
{
int x,y;
Point(){}
Point(int x,int y):x(x),y(y){}
}sta[320];
bool link[320][320];
double mp[320][320];
double dis[320];
bool vis[320];
double ans;
struct node
{
int to;
double cap;
node(){}
node(int to,double cap):to(to),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
};
double dis1(Point x,Point y)
{
return (double)sqrt((double)(x.x-y.x)*(x.x-y.x)+(double)(x.y-y.y)*(x.y-y.y));
}
void dijkstra()
{
memset(vis,false,sizeof(vis));
for(int i=0;i<k;i++)dis[i]=inf;
dis[0]=0;
priority_queue<node>q;
q.push(node(0,0));
while(!q.empty())
{
node s=q.top();q.pop();
if(vis[s.to])continue;
vis[s.to]=true;
for(int i=0;i<k;i++)
{
if(!vis[i]&&dis[i]>dis[s.to]+mp[s.to][i])
{
dis[i]=dis[s.to]+mp[s.to][i];
q.push(node(i,dis[i]));
}
}
}
ans=dis[1];
return ;
}
int main()
{
int sx,sy,ex,ey;
memset(link,false,sizeof(link));
for(int i=0;i<320;i++)
for(int j=0;j<320;j++)
mp[i][j]=inf;
scanf("%d %d %d %d",&sx,&sy,&ex,&ey);
int cnt=1;
int bx,by;
sta[k++]=Point(sx,sy);
sta[k++]=Point(ex,ey);
int ahead=k;
while(scanf("%d %d",&bx,&by)!=EOF)
{
sta[k++]=Point(bx,by);
while(scanf("%d %d",&bx,&by)!=EOF&&(bx!=-1&&by!=-1))
{ sta[k]=Point(bx,by);
mp[k][k-1]=mp[k-1][k]=min(mp[k][k-1],3.0*dis1(sta[k],sta[k-1])/2000.0);
k++;
}
}
for(int i=0;i<k;i++)
{ mp[i][i]=0;
for(int j=0;j<k;j++)
{
mp[i][j]=mp[j][i]=min(mp[i][j],3.0*dis1(sta[i],sta[j])/500.0);
}
}
dijkstra();
int tans=(int)(ans+0.5);
printf("%d\n",tans);
return 0;
}
https://cn.vjudge.net/problem/LightOJ-1074
很经典的问题,跑一遍SPFA就好了,判断负环
//写的时候发现自己的代码很不规范,最后因为if后面加了个;号·找了半天
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=220;
const int maxm=maxn*maxn;
int cnt,dis[maxn],w[maxn];
bool vis[maxn],cir[maxn];
int now[maxn];
int n,m;
struct node
{
int v,cap,next;
}edge[maxm];
int head[maxm];
void add(int u,int v,int cap)
{
edge[cnt].v=v;
edge[cnt].cap=cap;
edge[cnt].next=head[u];
head[u]=cnt;
//cnt++;
}
void dfs(int u)
{
cir[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next)
{
if(!cir[edge[i].v])
dfs(edge[i].v);
}
}
void SPFA()
{
for(int i=0;i<=n;i++)
{
dis[i]=inf;
}
memset(vis,false,sizeof(vis));
memset(now,0,sizeof(now));
memset(cir,false,sizeof(cir));
stack<int>q;
while(!q.empty())q.pop();
q.push(1);
dis[1]=0;
vis[1]=1;
now[1]=1;
while(!q.empty())
{
int s=q.top();q.pop();
//s=head[s];
vis[s]=false;
for(int i=head[s];i!=-1;i=edge[i].next)
{ //int to=head[i];
int v=edge[i].v;
int cap=edge[i].cap;
if(dis[v]>(dis[s]+cap)&&!cir[s])
{
dis[v]=dis[s]+cap;
if(!vis[v])
{
vis[v]=true;
now[v]++;
if(now[v]>=n&&!cir[v])
dfs(v);
else
q.push(v);
}
}
}
}
return ;
}
int main()
{
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{ cnt=0;
//memset(mp,0x3f3f3f3f,sizeof(mp));
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d %d",&u,&v);
add(u,v,(w[v]-w[u])*(w[v]-w[u])*(w[v]-w[u]));
cnt++;
}
SPFA();
int num;
scanf("%d",&num);
printf("Case %d:\n",cas);
for(int i=0;i<num;i++)
{
int q;
scanf("%d",&q);
if(dis[q]<3||dis[q]==inf||cir[q])
{
printf("?\n");
}
else
{
printf("%d\n",dis[q]);
}
}
}
}
https://cn.vjudge.net/problem/HDU-4725
建图思想比较好···一开始建崩了,看了题解,发现真的挺好的想法,就是每一层增加两个点,一个出口一个入门,也就是把每一层并成一个集合,就可以当作一个点。入口到当前层的每个点距离为0,出口连接别的上下层的入口,距离为3,然后跑dij就好了
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
#define ll long long
#define maxn 312345
#define inf 0x3f3f3f3f
bool vis[maxn];
int dis[maxn],n,m,cnt;
//bool vist[maxn];
struct node
{
int v,cap,next;
node(){}
node(int v,int cap):v(v),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
}edge[maxn*3];
int head[maxn*3];
//vector<int>g[maxn];
void add(int u,int v,int cap)
{
edge[cnt].v=v;
edge[cnt].cap=cap;
edge[cnt].next=head[u];
head[u]=cnt;
cnt++;
}
int dijkstra()
{
//for(int i=0;i<=n;i++)dis[i]=inf;
memset(dis,inf,sizeof(dis));
memset(vis,false,sizeof(vis));
priority_queue<node>q;
dis[1]=0;
q.push(node(1,0));
while(!q.empty())
{
node s=q.top();q.pop();
int u=s.v;
if(vis[s.v])continue;
vis[s.v]=true;
for(int i=head[s.v];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
int cap=edge[i].cap;
if(!vis[v]&&dis[v]>dis[u]+cap)
{
dis[v]=dis[u]+cap;
q.push(node(v,dis[v]));
}
}
}
return dis[n];
}
int main()
{
int T;
scanf("%d",&T);
for(int cas=1;cas<=T;cas++)
{ cnt=0;
//for(int i=0;i<3*n;i++)
//{
// edge[i].v=0;
// edge[i].next=0;
// edge[i].cap=0;
//}
memset(head,-1,sizeof(head));
//memset(vist,-1,sizeof(vist));
int c,t;
scanf("%d %d %d",&n,&m,&c);
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
//vist[t]=1;
add(2*t-1+n,i,0);//入口
add(i,2*t+n,0);//出口
}
for(int i=0;i<m;i++)
{
int u,v,cap;
scanf("%d %d %d",&u,&v,&cap);
add(u,v,cap);
add(v,u,cap);
}
for(int i=1;i<=n;i++)
{
int u=n+i*2;
if(i>1)
{
int v=u-3;
add(u,v,c);
}
if(i<n)
{
int v=u+1;
add(u,v,c);
}
}
int ans=dijkstra();
printf("Case #%d: %d\n",cas,ans==inf?-1:ans);
}
}
https://cn.vjudge.net/problem/HDU-3416
非常非常好的一道题,网络流的最大流+最短路。
题解:先从A找最短路dist1,再从B找最短路dist2,然后确定最短路径包含的边,只要dist1[a[i]]+dist2[b[i]]+c[i]==dis[A][B]那么就把这条边加入网络流的队伍中,最后跑一下网络流,然后注意一个地方,跑dinic的时候,每次bfs都会改变head的值,所以每次都要cpy一遍然后再dfs,不然会T到自闭(不要问我咋知道的)
AC代码,dij也可以
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2010;
const int maxm=2010000;
int cnt,ccnt,n,m;
int a[maxm],b[maxm],c[maxm],deep[maxn];
struct node
{
int to,cap,next;
node(){}
node(int v,int cap):to(v),cap(cap){}
bool operator < (const node &a)const
{
return cap>a.cap;
}
}edge[maxm],edge1[maxm];
int head[maxm],dis[maxn],dist1[maxn],dist2[maxn],head1[maxm],cur[maxm];
bool vis[maxn];
void add(int u,int v,int cap)
{
edge[cnt].to=v;edge[cnt].cap=cap;edge[cnt].next=head[u];head[u]=cnt++;
edge[cnt].to=u;edge[cnt].cap=cap;edge[cnt].next=head[v];head[v]=cnt++;
}
void addedge(int u,int v,int cap)
{
edge1[ccnt].to=v;edge1[ccnt].cap=cap;edge1[ccnt].next=head1[u];head1[u]=ccnt++;
}
void init()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void init1()
{
memset(head1,-1,sizeof(head1));
ccnt=0;
}
void dijkstra(int s)
{
priority_queue<node>q;
memset(dis,inf,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[s]=0;
q.push(node(s,0));
while(!q.empty())
{
node s=q.top();q.pop();
int u=s.to;
//int cap=s.cap;
if(vis[u])continue;
vis[u]=true;
for(int i=head1[u];i!=-1;i=edge1[i].next)
{
int v=edge1[i].to;
int cap=edge1[i].cap;
if(!vis[v]&&dis[v]>dis[u]+cap)
{
dis[v]=dis[u]+cap;
q.push(node(v,dis[v]));
}
}
}
}
/*void SPFA(int s)
{
for(int i=0;i<=n;i++)
{
dis[i]=inf;
vis[i]=false;
}
queue<int>q;
q.push(s);
dis[s]=0;
vis[s]=1;
while(!q.empty())
{
int s=q.front();q.pop();
vis[s]=false;
for(int i=head1[s];i!=-1;i=edge1[i].next)
{
int v=edge1[i].to;
int cap=edge1[i].cap;
if(dis[v]>dis[s]+cap)
{
dis[v]=dis[s]+cap;
if(!vis[v])
{
vis[v]=true;
q.push(v);
}
}
}
}
return ;
}*/
bool bfs(int s,int t)
{
queue<int>q;
memset(deep,-1,sizeof(deep));
deep[s]=0;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
int cap=edge[i].cap;
if(cap&&deep[v]==-1)
{
deep[v]=deep[u]+1;
q.push(v);
}
}
}
if(deep[t]!=-1)return true;
return false;
}
int dfs(int now,int t,int limit)
{
if(now==t)return limit;
for(int &i=cur[now];i!=-1;i=edge[i].next)
{
int u=edge[i].to;
int cap=edge[i].cap;
if(cap>0&&deep[now]+1==deep[u])
{
int f=dfs(u,t,min(limit,cap));
if(f>0)
{
edge[i].cap-=f;
edge[i^1].cap+=f;
return f;
}
}
}
return 0;
}
int dinic(int s,int t)
{
int flow=0;
while(bfs(s,t))
{ for(int i=1;i<=n;i++)cur[i]=head[i];
while(1)
{
int f=dfs(s,t,inf);
if(!f)break;
flow+=f;
}
}
return flow;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
int A,B;
for(int i=0;i<m;i++)
scanf("%d %d %d",&a[i],&b[i],&c[i]);
scanf("%d %d",&A,&B);
init1();
for(int i=0;i<m;i++)
addedge(a[i],b[i],c[i]);
dijkstra(A);
//SPFA(A);
memcpy(dist1,dis,sizeof(dis));
init1();
for(int i=0;i<m;i++)
addedge(b[i],a[i],c[i]);
dijkstra(B);
//SPFA(B);
memcpy(dist2,dis,sizeof(dis));
init();
for(int i=0;i<m;i++)
if(a[i]!=b[i]&&dist1[a[i]]+dist2[b[i]]+c[i]==dist1[B])
add(a[i],b[i],1);
//memcpy(head3,head,sizeof(head1));
//bfs(A,B);
//dfs(edge3,head3,A,B);
int ans=dinic(A,B);
printf("%d\n",ans);
}
return 0;
}
亲测 SPFA和dij复杂度都够,不过建议dij,SPFA有时候会被卡。
收获很大,加油加油!!!
这两题都是建图很好的例子了
https://cn.vjudge.net/problem/HDU-4370
https://cn.vjudge.net/problem/POJ-3169
首先第一题,用思维转换成图论。
自己在草稿纸上比划一下就知道 取得点的结果一定是这样的
例如n=4时
取(1,3)->(3,2)->(2,4)不难发现,这是一条路径1->3>2>4。从1-n的一条路径
这题也就转化成最短路了;
然而,还有一种情况。(1,3)->(3,1)这样的话,这个就被独立出来了,而我们只有第一行选择了一个1,所以还有从右边的第一列选择一个1,所以也就是(4,2)->(2,4)两个加起来才满足题目的情况。其他的情况已经被一开始的最短路包含了
所以就跑两次最短路
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=500;
int mp[maxn][maxn];
bool vis[maxn];
int dis[maxn];
int n;
void SPFA(int s)
{ queue<int>q;
for(int i=1;i<=n;i++)
{
if(i==s)
{
dis[i]=inf;
vis[i]=false;
}
else
{
dis[i]=mp[s][i];
q.push(i);
vis[i]=true;
}
}
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=false;
for(int i=1;i<=n;i++)
{
if(dis[i]>dis[u]+mp[u][i])
{
dis[i]=dis[u]+mp[u][i];
if(!vis[i])
{
vis[i]=true;
q.push(i);
}
}
}
//vis[u]=false;
}
}
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 ans=dis[n];
int loop1=dis[1];
SPFA(n);
int loopn=dis[n];
ans=min(ans,loop1+loopn);
printf("%d\n",ans);
}
}
https://cn.vjudge.net/problem/POJ-3169
这一道题,不放在最短路里的话确实很难让人想到。
思路:排序肯定为1-n,那么相互吸引的增加负边,相互有好感的增加正边,跑一边最短路,用SPFA判负环。
负环出现的情况就是不可能存在正确的排列的情况。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<string>
//#include<regex>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
#define inf 0x3f3f3f3f
int dis[maxn];
bool vis[maxn];
struct node
{
int to,cap,next;
node(){}
node(int to,int cap):to(to),cap(cap){}
}edge[maxn];
int head[maxn],tot,cnt[maxn],n,m,l;
void add(int u,int to,int cap)
{
edge[tot].to=to;
edge[tot].cap=cap;
edge[tot].next=head[u];
head[u]=tot++;
}
bool SPFA(int s)
{
queue<int>q;
memset(dis,inf,sizeof(vis));
memset(cnt,0,sizeof(cnt));
memset(vis,false,sizeof(vis));
dis[s]=0;
cnt[s]=1;
vis[s]=true;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=false;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].cap)
{
dis[v]=dis[u]+edge[i].cap;
if(!vis[v])
{
vis[v]=true;
q.push(v);
if(++cnt[v]>n)return false;
}
}
}
}
return true;
}
int main()
{ memset(head,-1,sizeof(head));
scanf("%d %d %d",&n,&m,&l);
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
if(u>v)swap(u,v);
add(u,v,w);
}
for(int i=0;i<l;i++)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
if(u>v)swap(u,v);
add(v,u,-w);
}
if(!SPFA(1))printf("-1\n");
else if(dis[n]==inf) printf("-2\n");
else printf("%d\n",dis[n]);
}