历时三天,终于ac。。。第一次这么晚睡觉呵呵。。。
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=109960#problem/F
题意:
直接翻译:有个出发点,然后有很多个公交站台,从一个站台到另一个站台需要花钱,求从初始点到所有站台,然后再回来 最少需要多少钱
浓缩成题意:有n个点,m条边的有向图 ,求从初始点到所有点的长度加上从所有点回到起始点的长度
耐心看完前面的吐槽,后面有对ac代码的耐心讲解,嘿嘿。。。
队友说是模板题,10分钟就搞定了。。。然而我竟然没注意到数据量是1000000,开二维数组mle
代码如下:
#include <cstring> #include <cstdio> #include <queue> #include <iostream> #include <vector> #include <deque> #include <utility> #include <functional> const int maxnum = 1000; #define maxn 10000 using namespace std; int dis[maxn][maxn]; int book[maxnum] ,n; typedef pair <int ,int> pii; priority_queue <pii,vector<pii> ,greater <pii> > pq; void ppqq(int lon1[]) { int node ,flag = 0 ,lon ; while (!pq.empty()) { lon = pq.top().first; node = pq.top().second; pq.pop(); if(book[node] == 1) continue; lon1[node] = lon; book[node] = 1; flag++; if(flag == n) break; for (int i = 1 ; i <= n ;i++) { if( !book[i] && dis[node][i]) pq.push(make_pair(lon+dis[node][i] , i)); } } return ; } void ppq(int lon2[]) { int node ,flag = 0 ,lon ; while (!pq.empty()) { lon = pq.top().first; node = pq.top().second; pq.pop(); if(book[node] == 1) continue; lon2[node] = lon; book[node] = 1; flag++; if(flag == n) break; for (int i = 1 ; i <= n ;i++) { if( !book[i] && dis[i][node]) pq.push(make_pair(lon+dis[i][node] , i)); } } return ; } int main() { int T; int lon1[maxnum],lon2[maxnum] ; scanf("%d",&T); while (T--) { int m,u,v,w; scanf("%d%d",&n,&m); memset(dis,0,sizeof(dis)); memset(lon1,0,sizeof(lon1)); memset(lon2,0,sizeof(lon2)); memset(book,0,sizeof(book)); for (int i = 0 ; i < m ;i++) { scanf("%d%d%d",&u,&v,&w); dis[u][v] = w; } pq.push(make_pair(0,1)); ppqq(lon1); while (!pq.empty()) pq.pop(); memset(book,0,sizeof(book)); pq.push(make_pair(0,1)); ppq(lon2); int ans = 0; for (int i = 1 ; i<= n ;i++) { ans += lon1[i] + lon2[i]; printf("%d %d\n",lon1[i],lon2[i]); } printf("%d\n",ans); } return 0; }也对啊,这么大数据量开这么多数组,1000000 * 1000000,不mle我都不信。然后就用了结构体。。。然后tle了。。
也是醉了啊,写的时候也是各种错误,查了好久,尤其两次调用,已经改变了的结构体的成员值不知道怎么办了,后来又开了个数组减回去,虽然tle也算白写吧
代码如下:
#include <cstdio> #include <iostream> #include <queue> #include <vector> #include <cstring> const int maxnum = 1000010; using namespace std; struct node { int u,v,w; bool operator < (const node &a) const { return w > a.w; } }; priority_queue <node> pq; int ans = 0, n,m,book[maxnum]; struct node edg[maxnum]; int s[maxnum]; void dj1(struct node edg[]) { int node ,flag = 0 ,lon ; while (!pq.empty()) { lon = pq.top().w; node = pq.top().v; pq.pop(); if(book[node] == 1) continue; ans += lon; // printf("dj1 %d\n",lon); book[node] = 1; flag++; if(flag == n) break; for (int i = 1 ; i <= m ;i++) { // printf("qishidian%d %d\n",edg[i].u,edg[i].v); if( !book[edg[i].v] && edg[i].u == node) { // printf("qishidian%d %d\n",edg[i].u,edg[i].v); edg[i].w += lon ; s[i] += lon; pq.push(edg[i]); } } } return ; } void dj2(struct node edg[]) { int node ,flag = 0 ,lon ; while (!pq.empty()) { lon = pq.top().w; node = pq.top().u; pq.pop(); if(book[node] == 1) continue; ans += lon; //printf("dj2 nod = %d lon = %d\n",node ,lon); book[node] = 1; flag++; if(flag == n) break; for (int i = 1 ; i <= m ;i++) { //printf("一定要找到你%d %d %d\n",edg[i].u,edg[i].v,edg[i].w); if( !book[edg[i].u] && edg[i].v == node) { // printf("目前的%d %d %d\n",edg[i].u,edg[i].v,edg[i].w); edg[i].w += lon; // printf("改变后%d %d %d\n",edg[i].u,edg[i].v,edg[i].w); pq.push(edg[i]); } } } return ; } int main() { int t; scanf("%d",&t); while (t--) { ans = 0; memset(s,0,sizeof(s)); memset(book,0,sizeof(book)); scanf("%d%d",&n,&m); for (int i = 1 ; i <= m ;i++) scanf("%d%d%d",&edg[i].u,&edg[i].v,&edg[i].w); edg[0].u = 1; edg[0].v = 1; edg[0].w = 0; pq.push(edg[0]); dj1(edg); while (!pq.empty()) pq.pop(); memset(book,0,sizeof(book)); for (int i = 1 ; i <= m ;i++) edg[i].w -= s[i]; pq.push(edg[0]); dj2(edg); printf("%d\n",ans); } }哦哦哦、、、你还在看,那就给你看ac的代码吧,说实话,这回真是模板了,就像刚说的,结构体成员值改变了就是不可逆的了,下次调用不好玩,所以可以结合一下啊,push的都是pair《int,int》,这样子就不会改变了,然后第二次调用同一个函数的话可以少写好多的说,怎么做到呢,只能将这个图图整个反过来,这时候求出的1到所有点的最短路就是要求的原图的所有点到起始点的最短距离了。虽然是挑战上的模板 ,但还是说一下算法,以后也方便想来。。。vector是个不定长的数组,里面每个成员都是个结构体,结构体只存终点和距离,那起点呢 ? 你会发现vector又给了个数组。。。目的就是把起点都是 i 的点都放在vector【 i 】里 ,比如点3 到2 5 8点都有距离,vector【3】【0】,vector【3】【1】 , vector【3】【2】就分别保存着这几个结构体了,这样知道源点(3)就可以快速不多循环的找到这几个和3相连的点点。。。。pair这三个代码都是一样的,first值是保存源点到这个点的距离,second是这是哪个点,然后,优先队列,小顶堆,肯定能做到最短路了 ,d数组就是记录到每个点的最短路长,初始化INT_MAX,如果更新到这个点,发现曾经更新的比这次的小,那这次pop后就可以直接玩下一个队头元素了,保证d里面放的是最短路径长度,然后,,,,没什么了吧。。。。
给代码:
#include <climits> #include <iostream> #include <cstdio> #include <vector> #include <queue> #include <functional> #include <utility> #define maxnum 1000010 using namespace std; struct edg { int to; int dis; }; typedef pair <int ,int > pii; vector <edg> from[maxnum]; int d[maxnum]; int u[maxnum],v[maxnum],w[maxnum]; priority_queue < pii , vector <pii> ,greater <pii> > pq; void dij1() { while ( !pq.empty()) { int ss = pq.top().first; int node = pq.top().second; pq.pop(); if(d[node] < ss ) continue; for (int i = 0 ; i < from[node].size();i++) { edg e = from[node][i]; if(d[e.to] > d[node] + e.dis) { d[e.to] = d[node] + e.dis; pq.push(make_pair(d[e.to],e.to)); } } } } int main() { int t; scanf("%d",&t); while (t--) { int n , m ; scanf("%d%d",&n,&m); long long int ans = 0; for (int i = 0 ; i < m ;i++) scanf("%d%d%d",&u[i],&v[i],&w[i]); for (int i = 0 ; i < m ;i++) { edg e; e.to=v[i]; e.dis=w[i]; from[u[i]].push_back(e); } for (int i = 1 ; i <= n ;i++) d[i] = INT_MAX; pq.push(make_pair(0,1)); d[1] = 0; dij1(); for (int i = 1 ;i <= n ;i++) ans += d[i]; for (int i = 0 ;i < m ;i++) from[i].clear(); for (int i = 1 ; i <= n ;i++) d[i] = INT_MAX; for (int i = 0 ; i < m ;i++) { edg e; e.to=u[i]; e.dis=w[i]; from[v[i]].push_back(e); } pq.push(make_pair(0,1)); d[1] = 0; dij1(); for (int i = 1 ;i <= n ;i++) ans += d[i]; printf("%I64d\n",ans); for (int i = 0 ;i < m ;i++) from[i].clear(); } return 0; }要清空vector啊!!!!wa了一发,大半夜的多吓人啊。。。