题目大意:给出一个F代表农场的个数,其实就是测试样例组数,给出个N代表每个农场包含N个厂区,M代表N个厂区之间之间的路径条数,W表示有W个虫洞
题目上说路径是双向的,虫洞是单向的,意味着是有向图,并且说虫洞从一个顶点到另一个顶点让时间倒流,意味着路的权值是负值,接下来的M行是路径的条数,并且题目已经说明是一个厂区到另一个厂区不一定只有一条路(Two fields might be connected by more than one path.),接下来的W行是虫洞的起始和结束位置,还有让时间倒退的值。
解决:首先是建图,我用邻接矩阵,建图的时候要注意两个厂区之间不一定只有一条路,要添加一条语句让最小的进图,M个是双向的,W个是单向的,单向的不用判断直接赋值肯定没有比
它的值还小的。然后是找负回路用的是 spfa
#include <iostream> #include <queue> #include <cstring> using namespace std; const int N=3000; const int MAX=0x3f3f3f3f; int cost[N][N]; int inq[N]; int dist[N]; int n,m,w; int cnt[N]; bool spfa(int v) { int i; memset(dist,0x3f3f,sizeof(dist)); memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); int v0=v; dist[v0]=0; queue<int> q; q.push(v0); inq[v0]=1; // 源点进队列与不进队列虽然关系不大,但是进队列可以缩短运行时间,每进一次可是不要小看, //这个点面对的是所有的边,所以进队列要将cnt的值置为1 cnt[v0]=1; while(!q.empty()) { v0=q.front(); q.pop(); inq[v0]=0; for(i=1;i<=n;i++) { if(cost[v0][i]<MAX && dist[v0]+cost[v0][i]<dist[i]) { dist[i]=dist[v0]+cost[v0][i]; if(!inq[i]) { inq[i]=1; q.push(i); //共有n条边,等于结点总数返回,大于当然可以,但是何必呢。费时不是 if(++cnt[i]==n)return true; } } } } return false; } int main() { int icase,i,a,b,val; scanf("%d",&icase); while(icase--) { memset(cost,0x3f3f,sizeof(cost)); scanf("%d%d%d",&n,&m,&w); for(i=0;i<m;i++) {//输入时比较一下,由于邻接矩阵两个顶点只能放一条边,只好选最短边放了 scanf("%d%d%d",&a,&b,&val); if(cost[a][b]>val){cost[a][b]=cost[b][a]=val;} } for(i=0;i<w;i++) {//这个地方也是可以判断一下,选最短的边放,万一两个厂区之间有两个洞呢 scanf("%d%d%d",&a,&b,&val); cost[a][b]=-val; }//自己假设的1是源点,若是不连通,就惨了,
if(spfa(1)){puts("YES");} else puts("NO"); } system("pause"); return 0; }
虽然ac了,但是感觉漏洞百出,只是数据通过了,不只是为了通过数据,就万事大吉了。
#include <queue> #include <cstring> const int MAX=0x3f3f3f3f; using namespace std; const int N=3000; int cost[N][N]; int inq[N]; int dist[N]; int n,m,w; int cnt[N]; bool spfa() { int i; memset(dist,0,sizeof(dist)); memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); int v0; //为了防止图不连通。将所有顶点入队,并且初始化dist为0,意味着 //虚拟源点到各个顶点的距离为0 queue<int> q; for(int i=1;i<=n;i++)q.push(i);//虽然进队,但是不能cnt[i]++, while(!q.empty()) { v0=q.front(); q.pop(); inq[v0]=0; for(i=1;i<=n;i++) { if(cost[v0][i]<MAX && dist[v0]+cost[v0][i]<dist[i]) { dist[i]=dist[v0]+cost[v0][i]; if(!inq[i]) { inq[i]=1; q.push(i);//判断的时候还要是n,虚拟的无视 if(++cnt[i]==n)return true; } } } } return false; } int main() { int icase,i,a,b,val; scanf("%d",&icase); while(icase--) { memset(cost,0x3f3f,sizeof(cost)); scanf("%d%d%d",&n,&m,&w); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&val); if(cost[a][b]>val){cost[a][b]=cost[b][a]=val;} } for(i=0;i<w;i++) { scanf("%d%d%d",&a,&b,&val); cost[a][b]=-val; } if(spfa()){puts("YES");} else puts("NO"); } system("pause"); return 0; }
以上的程序虽然都可以但是时间复杂度已经达到了1500ms,以下用spfa+邻接表实现。
#include <iostream> #include <queue> #include <cstdio> using namespace std; const int N=510; struct node { int v,w,next; }; node e[50000]; int n,m,w,ss,ee,t; int pos; int head[N]; int cnt[N]; int dist[N]; bool inq[N]; void init() { memset(head,-1,sizeof(head)); memset(dist,0,sizeof(dist)); memset(cnt,0,sizeof(cnt)); memset(inq,0,sizeof(inq)); } void addedge(int u,int v,int w) { e[pos].v=v; e[pos].w=w; e[pos].next=head[u]; head[u]=pos++; } bool spfa() { int v0,v,w,i; queue<int> q;//虚拟顶点各个顶点入队 for(i=1;i<=n;i++)q.push(i); while(!q.empty()) { v0=q.front(); q.pop(); inq[v0]=false; for(i=head[v0];i!=-1;i=e[i].next) { v=e[i].v; w=e[i].w; if(dist[v0]+w<dist[v]) { dist[v]=dist[v0]+w; if(!inq[v]) { inq[v]=1; q.push(v); if(++cnt[v]==n)return true; } } } } return false; } int main() { int i,f; scanf("%d",&f); while(f--) { pos=0; scanf("%d%d%d",&n,&m,&w); init();//就因为init放在了scanf前面导致不断错误,并提出了可笑的问题 for(i=0;i<m;i++) { scanf("%d%d%d",&ss,&ee,&t); addedge(ss,ee,t); addedge(ee,ss,t); } for(i=0;i<w;i++) { scanf("%d%d%d",&ss,&ee,&t); addedge(ss,ee,-t); } if( spfa() )puts("YES"); else puts("NO"); } system("pause"); return 0; }