历时三天,终于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了一发,大半夜的多吓人啊。。。