POJ 1724
一个优先队列+dij 一个dfs+剪枝
dfs更好理解,不过优先队列留着,以后在学习学习
dfs版本:
一个优先队列+dij 一个dfs+剪枝
dfs更好理解,不过优先队列留着,以后在学习学习
dfs版本:
- #include<iostream>
- using namespace std;
- const int inf=1e7;
- const int CitySize=101;
- const int RoadSize=10001;
- struct
- {
- int S,D,L,T; //Source,Destination,Length,Toll
- int next; //指向相同Source的下一条边
- }road[RoadSize];
- int pr; //road[]指针
- int K,N,R; //Money,CityNum,RoadNum
- int MinLen;
- bool vist[CitySize];
- int ListTable_Head[CitySize]; //邻接链表头指针数组
- void DFS(int NowCity,int NowLen,int RestMoney)
- {
- if(NowLen>MinLen)
- return;
- if(NowCity==N && RestMoney>=0 && NowLen<MinLen)
- {
- MinLen=NowLen;
- return;
- }
- for(int i=ListTable_Head[NowCity];i!=-1;i=road[i].next)
- {
- int tD=road[i].D;
- int tL=road[i].L;
- int tT=road[i].T;
- if(!vist[tD] && RestMoney>=tT)
- {
- vist[tD]=true;
- DFS(tD,NowLen+tL,RestMoney-tT);
- vist[tD]=false;
- }
- }
- return;
- }
- int main(void)
- {
- while(cin>>K>>N>>R)
- {
- memset(ListTable_Head,-1,sizeof(ListTable_Head));
- memset(vist,false,sizeof(vist));
- pr=0;
- MinLen=inf;
- for(int i=1;i<=R;i++)
- {
- int s,d,l,t;
- cin>>s>>d>>l>>t;
- road[pr].S=s;
- road[pr].D=d;
- road[pr].L=l;
- road[pr].T=t;
- road[pr].next=ListTable_Head[s];
- ListTable_Head[s]=pr++;
- }
- DFS(1,0,K);
- cout<<(MinLen<inf?MinLen:-1)<<endl;
- }
- return 0;
- }
- #include <cstdio>
- #include <cstring>
- #include <vector>
- #include <queue>
- #include <algorithm>
- using namespace std;
- #define N 110
- #define M 10010
- #define INF 0x3f3f3f3f
- int n,m,cost,tot;
- struct State
- {
- int n,d,c;
- bool operator < (const struct State a)const
- {
- if(a.d == d) return a.c < c;
- return a.d < d;
- }
- };
- struct edge
- {
- int u,v,w,c,next;
- };
- typedef struct State State;
- typedef struct edge edge;
- int head[N];
- int d[N];
- edge e[M];
- void add(int u , int v , int w , int c)
- {
- e[tot].u = u; e[tot].v = v; e[tot].w = w; e[tot].c = c;
- e[tot].next = head[u]; head[u] = tot++;
- }
- void Dij()
- {
- priority_queue<State>q;
- State sta;
- int res = INF ;
- memset(d,0x3f,sizeof(d));
- while(!q.empty()) q.pop();
- sta.d = 0;
- sta.n = 1;
- sta.c = 0;
- q.push(sta);
- while(!q.empty())
- {
- State x,y;
- int u,v,w,d,c;
- x = q.top(); q.pop();
- u = x.n; d = x.d;
- if(u == n)
- {
- res = x.d;
- break;
- }
- for(int k=head[u]; k!=-1; k=e[k].next)
- {
- v = e[k].v; w = e[k].w; c = e[k].c;
- if(x.c + c <= cost) //在花费允许的范围内可以去到这个点
- {
- y.n = v; y.d = d + w; y.c = x.c + c;
- q.push(y);
- }
- }
- }
- if(res == INF) printf("-1\n");
- else printf("%d\n",res);
- }
- int main()
- {
- scanf("%d%d%d",&cost,&n,&m);
- memset(head,-1,sizeof(head));
- tot = 0;
- while(m--)
- {
- int u,v,w,c;
- scanf("%d%d%d%d",&u,&v,&w,&c);
- add(u,v,w,c);
- }
- Dij();
- return 0;
- }
注意这道题是求最大流量最短路,刚刚那道题是求最短时间最短路。
①二分+Dij最短路
题目大意:
有N个点,点1为珍贵矿物的采矿区, 点N为加工厂,有M条双向连通的边连接这些点。走每条边的运输容量为C,运送时间为D。
他们要选择一条从1到N的路径运输, 这条路径的运输总时间要在T之内,在这个前提之下,要让这条路径的运输容量尽可能地大。
一条路径的运输容量取决与这条路径中的运输容量最小的那条边。
分析与总结:
因为每条路径的容量取决于这条路径中所有边中的最小容量,所以我们可以以此枚举最小容量。
但是如果一个一个容量的枚举,那明显效率太低了。
通过分析,可以看出,如果最低容量越大,那么符合要求的路径就越少,所以,根据容量的大小,路径数量是单调的。
有了单调性,就可以利用二分法了。
只要把容量从大到小进行排序,然后二分之,很快便能算出答案。- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<queue>
- using namespace std;
- const int INF = 0x7fffffff;
- const int VN = 10005;
- const int EN = 50005;
- struct Edge{int v,next,cap,time;}E[EN*2];
- int n, m, t;
- int size;
- int head[VN];
- int cap[EN];
- int d[VN];
- int Time[VN];
- int limit;
- bool inq[VN];
- void init(){
- size=0;
- memset(head, -1, sizeof(head));
- }
- void addEdge(int u,int v,int c,int d){
- E[size].v=v;
- E[size].cap=c;
- E[size].time=d;
- E[size].next = head[u];
- head[u] = size++;
- }
- int Dijkstra(int src){
- memset(inq, 0, sizeof(inq));
- for(int i=1; i<=n; ++i)d[i]=INF;
- d[src] = 0;
- queue<int>q;
- q.push(src);
- while(!q.empty()){
- int u = q.front(); q.pop();
- inq[u] = false;
- for(int e=head[u]; e!=-1; e=E[e].next)if(E[e].cap>=limit){
- int tmp = d[u]+E[e].time;
- if(d[E[e].v] > tmp){
- d[E[e].v] = tmp;
- if(!inq[E[e].v]){
- inq[E[e].v] = true;
- q.push(E[e].v);
- }
- }
- }
- }
- return d[n];
- }
- int main(){
- int T, u, v, c, d;
- scanf("%d",&T);
- while(T--){
- scanf("%d%d%d",&n,&m,&t);
- init();
- for(int i=0; i<m; ++i){
- scanf("%d%d%d%d",&u,&v,&c,&d);
- cap[i]=c;
- addEdge(u,v,c,d);
- addEdge(v,u,c,d); //有一点没理解为什么用两条边,难道是因为是无向边。。。
- }
- sort(cap, cap+m,greater<int>());
- // 二分求解
- int left=0, right=m-1, mid;
- while(left<right){
- mid = (left+right)>>1; //这里需要注意,最好用位运算比较快
- limit = cap[mid];
- int tmp=Dijkstra(1);
- if(tmp==INF || tmp>t){
- left = mid+1;
- }
- else{
- right=mid;
- }
- }
- printf("%d\n", cap[left]);
- }
- return 0;
- }
- #include<iostream>
- #include<algorithm>
- #include<cstdio>
- #include<cstring>
- #include<queue>
- using namespace std;
- #define MAXN 110010
- #define INF 0x7FFFFFFF
- int cnt , n , m , t;
- int first[MAXN] , next[MAXN];
- int star[MAXN] , end[MAXN];
- int value[MAXN] , Time[MAXN];
- int dis_Time[MAXN];
- int vis[MAXN];
- queue<int>q;
- void init(){
- memset(first , -1 , sizeof(first));
- memset(next , -1 , sizeof(next));
- }
- void SPFA(int s){
- memset(vis , 0 , sizeof(vis));
- for(int i = 2 ; i <= n ; i++)
- dis_Time[i] = INF;
- dis_Time[1] = 0;
- q.push(1);
- vis[1] = 1;
- while(!q.empty()){
- int x = q.front();
- q.pop();
- vis[x] = 0;
- for(int i = first[x] ; i != -1 ; i = next[i]){
- if(dis_Time[end[i]] > dis_Time[x] + Time[i] && s <= value[i]){/*这里加了一个边长都要大于s*/
- dis_Time[end[i]] = dis_Time[x] + Time[i];
- if(!vis[end[i]]){
- vis[end[i]] = 1;
- q.push(end[i]);
- }
- }
- }
- }
- }
- int main(){
- scanf("%d" , &cnt);
- while(cnt--){
- scanf("%d%d%d" , &n , &m , &t);
- init();
- for(int i = 0 ; i < m ; i++){
- scanf("%d%d%d%d" , &star[i] , &end[i] , &value[i] , &Time[i]);
- star[i+m] = end[i] , end[i+m] = star[i];
- value[i+m] = value[i] , Time[i+m] = Time[i];
- next[i] = first[star[i]];
- first[star[i]] = i;
- next[i+m] = first[star[i+m]];
- first[star[i+m]] = i+m;
- }
- /*二分最短路*/
- int tmp_value[MAXN];
- memcpy(tmp_value , value , sizeof(tmp_value));
- sort(tmp_value , tmp_value+m);
- int left , right , mid , ans;
- left = 0 , right = m-1;
- /*二分查找的形式*/
- while(left <= right){
- mid = (right+left)/2;
- SPFA(tmp_value[mid]);
- if(dis_Time[n] <= t)/*满足条件还要继续二分,因为不是最大*/
- ans = tmp_value[mid] , left = mid + 1;
- else
- right = mid - 1;
- }
- printf("%d\n" , ans);
- }
- return 0;
- }