本節內容——node
有n架飛機須要着陸。每架飛機均可以選擇「早着陸"和」晚着陸「兩種方式之一,且必須選擇一種。第i架飛機的早着陸時間爲\(E_i\),晚着陸時間爲\(L_I\),不得在其餘時間着陸。如今須要安排這些飛機的着陸方式,使得整個着陸計劃儘可能安全。換句話說,若是把全部飛機的實際着陸時間按照從早到晚的順序排列,相鄰兩個着陸時間間隔的最小值(成爲安全間隔)儘可能大。ios
也就是二分這個時間,而後判斷該2-SAT是否有解。(由於這個間隔時間越小就也可能有合法解,反之越不可能,因此能夠二分答案)
構圖的時候,若是兩個決策(好比說\(i_l,j_l\))間隔小於二分的答案,咱們就給\(i_l,j_r\)和\(i_r,j_l\)連有向邊。
而後跑tarjan判斷就好了,若是同一個飛機的兩個決策在一個強聯通份量裏面,就沒有合法解了。
順便一提,劉汝佳書上寫的那個作法複雜度是假的qwqc++
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define MAXN 4010 #define INF 0x3f3f3f3f using namespace std; int n,m,t,tot,cnt,top; int head[MAXN],dfn[MAXN],low[MAXN],in[MAXN],st[MAXN],c[MAXN]; struct Edge{int nxt,to;}edge[MAXN*MAXN]; struct Node{int l,r;}node[MAXN]; inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;} inline void tarjan(int x) { dfn[x]=low[x]=++tot; in[x]=1; st[++top]=x; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]); else if(in[v]) low[x]=min(low[x],dfn[v]); } if(dfn[x]==low[x]) { int v;++cnt; do{v=st[top--],c[v]=cnt,in[v]=0;}while(x!=v); } } inline bool check(int x) { memset(head,0,sizeof(head)); t=tot=top=cnt=0; for(int i=1;i<=(n>>1);i++) for(int j=1;j<=(n>>1);j++) { if(i==j) continue; int a=abs(node[i].l-node[j].l); int b=abs(node[i].l-node[j].r); int c=abs(node[i].r-node[j].l); int d=abs(node[i].r-node[j].r); if(a<x) add(i*2-1,j*2); if(b<x) add(i*2-1,j*2-1); if(c<x) add(i*2,j*2); if(d<x) add(i*2,j*2-1); } memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(in,0,sizeof(in)); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<n;i+=2) if(c[i]==c[i+1]) return false; return true; } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d%d",&node[i].l,&node[i].r); n<<=1; int l=0,r=INF,ans=0; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); } return 0; }
有A,B,C三種任務要分配給n個宇航員,其中每一個宇航員剛好要分配一個任務。設全部n個宇航員的平均年齡爲x,只有年齡大於或等於x的宇航員才能分配任務A;只有年齡嚴格小於x的宇航員才能分配任務B,而任務C沒有限制。有m對宇航員相互討厭,所以不能分配到統一任務。如今須要找出一個知足上訴全部要求的任務分配方案。算法
3-SAT???不可能的。咱們只要處理一下年齡,對於每一個宇航員,照樣是2-SAT.
而後就......和上面那題同樣作就好了啊??
可是爲何會RE啊......搞不懂......先把代碼貼上,回來找鍋(咕咕咕)安全
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define MAXN 100010 using namespace std; int n,m,all,kkk,cnt,tot,top,t; int head[MAXN],done[MAXN]; int dfn[MAXN],low[MAXN],in[MAXN],st[MAXN],c[MAXN]; char op[MAXN]; struct Node{int l,r,age;}node[MAXN]; struct Edge{int nxt,to;}edge[MAXN<<1]; struct Line{int u,v;}line[MAXN<<1]; inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;} inline void tarjan(int x) { low[x]=dfn[x]=++tot; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]); else if(in[v]) low[x]=min(low[x],dfn[v]); } if(low[x]==dfn[x]) { int v;++cnt; do{v=st[top--];in[v]=0;c[v]=++cnt;}while(x!=v); } } inline bool check() { memset(head,0,sizeof(head)); memset(in,0,sizeof(in)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); top=tot=t=cnt=0; for(int i=1;i<=m;i++) { int u=line[i].u,v=line[i].v; if(node[u].age^node[v].age) { add(node[u].r,node[v].l),printf("%d %d\n",node[u].r,node[v].l); add(node[v].r,node[u].l),printf("%d %d\n",node[v].r,node[u].l); } else { add(node[u].l,node[v].r),printf("%d %d\n",node[u].l,node[v].r); add(node[u].r,node[v].l),printf("%d %d\n",node[u].r,node[v].l); add(node[v].l,node[u].r),printf("%d %d\n",node[v].l,node[u].r); add(node[v].r,node[u].l),printf("%d %d\n",node[v].r,node[u].l); } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i),cout<<i<<endl; for(int i=1;i<=n;i++) if(c[node[i].l]==c[node[i].r]&&c[node[i].l]!=0) return false; return true; } inline bool solve(int x,int c) { done[x]=c,done[x^1]=3-c; printf("done[%d]=%d done[%d]=%d\n",x,done[x],x^1,done[x^1]); cout<<endl; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(done[v]&&done[v]==c) return false; else if(!done[v]) solve(v,c); } return true; } inline void print() { cout<<"yes"<<endl; for(int i=1;i<=n;i++) { if(done[node[i].l]==1) printf("%c\n",op[node[i].l]); else if(done[node[i].l]==0&&done[node[i].r]==0) printf("%c\n",op[node[i].l]); } } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(scanf("%d%d",&n,&m)==2) { if(n==0&&m==0) break; memset(head,0,sizeof(head)); top=tot=t=cnt=all=0; for(int i=1;i<=n;i++) scanf("%d",&node[i].age),all+=node[i].age; all/=n; for(int i=1;i<=n;i++) { if(node[i].age<all) node[i].age=0; else node[i].age=1; } for(int i=1;i<=n;i++) scanf("%d%d",&line[i].u,&line[i].v); for(int i=1;i<=n;i++) if(node[i].age==0) node[i].l=++kkk,op[kkk]='B',node[i].r=++kkk,op[kkk]='C'; else node[i].l=++kkk,op[kkk]='A',node[i].r=++kkk,op[kkk]='C'; for(int i=1;i<=n;i++) printf("%c %c\n",op[node[i].l],op[node[i].r]); if(check()==false) {printf("No solution.\n");continue;} memset(done,0,sizeof(done)); bool flag=true; for(int i=1;i<=n;i++) if(!done[i]) if(solve(node[i].l,1)) flag=false; if(flag==true) {print();continue;} memset(done,0,sizeof(done)); flag=true; for(int i=1;i<=n;i++) if(!done[i]) solve(node[i].r,1); print(); } return 0; }
機場快線分爲經濟線和商業線兩種,線路、速度和價錢都不一樣。如今你有一張商業線的車票,能夠坐一站商業線,而其餘時候只能乘坐經濟線。假設換成時間忽略不計,你的任務是找一條取機場最快的線路,而後輸出方案。(保證最優解惟一)this
由於商業線只能坐一站,並且數據範圍在1000之內,因此咱們能夠枚舉坐的是哪一站。
假設咱們用商業線車票從車站a坐到b,則從起點到a,從b到終點這兩部分的路線對於只存在經濟線的圖中必定是最短路。因此咱們只須要從起點、終點開始作兩次最短路,記錄下從起點到每一個點x的最短期\(f(x)\)和它到終點的最短期\(g(x)\),那麼總時間就是\(f(a)+time(a,b)+g(b)\)。spa
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; typedef long long ll; const double PI = acos(-1.0); const double eps = 1e-6; const int mod = 1000000000 + 7; const int INF = 1000000000; const int maxn = 500 + 10; int T,n,m,S,d1[maxn], p1[maxn], d2[maxn], p2[maxn], vis[maxn], ok, dist; struct node { int u, val; node(int u=0, int val=0):u(u), val(val) {} bool operator < (const node& rhs) const { return val > rhs.val; } }; void print(int root, int p[], int id, int S) { vector<int> ans; while(root != S) { ans.push_back(root); root = p[root]; } ans.push_back(root); int len = ans.size(); if(id == 1) for(int i=len-1;i>=0;i--) { if(i != len-1) printf(" "); printf("%d",ans[i]); } else for(int i=0;i<len;i++) { if(i != 0) printf(" "); printf("%d",ans[i]); } } vector<node> g[maxn]; void BFS(int haha, int d[], int p[]) { priority_queue<node> q; q.push(node(haha, 0)); for(int i=1;i<=n;i++) { d[i] = INF; } d[haha] = 0; memset(vis, false, sizeof(vis)); while(!q.empty()) { node u = q.top(); q.pop(); if(vis[u.u]) continue; vis[u.u] = true; int len = g[u.u].size(); for(int i=0;i<len;i++) { node v = g[u.u][i]; if(d[v.u] > d[u.u] + v.val) { d[v.u] = d[u.u] + v.val; p[v.u] = u.u; q.push(node(v.u, d[v.u])); } } } } int a,b,c,kase=0; int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(~scanf("%d%d%d",&n,&S,&T)) { scanf("%d",&m); for(int i=1;i<=n;i++) g[i].clear(); while(m--) { scanf("%d%d%d",&a,&b,&c); g[a].push_back(node(b, c)); g[b].push_back(node(a, c)); } scanf("%d",&m); ok = -1; BFS(S, d1, p1); BFS(T, d2, p2); int s = -1,t = -1,ans = d1[T], res = 0; for(int i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&c); if(cur < ans) { ans = cur; s = b; t = a; } } if(kase) printf("\n"); else ++kase; if(s > 0) { print(s, p1, 1, S); printf(" "); print(t, p2, 2, T); printf("\n"); printf("%d\n",s); } else { print(T, p1, 1, S); printf("\n"); printf("Ticket Not Used\n"); } printf("%d\n",ans); } return 0; }
書上還提到了dij算法的路徑統計,在這裏就簡單說一下吧
枚舉兩點之間的全部最短路:先求出全部點到目標點的最短路長度\(d[i]\),而後從起點開始出發,只沿着\(d[i]=d[j]+dist(i,j)\)的邊走。
兩點之間的最短路計數:令\(f[i]\)表示從i到目標點的最短路的條數,則\(f[i]=\sum f[j] | d[i]=d[j]+dist(i,j)\)(這裏書上寫錯了).net
對於一張圖,只沿着知足以下條件的道路(A,B)走:存在一條從B出發回家的路徑,比全部從A出發回家的路徑都短。問不一樣的回家路徑條數。翻譯
先跑完以家爲源點的最短路,而後若是一個點a的最短路比b的小,那麼連邊,這就是一個DAG了,而後再DP計個數就好了。code
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #define MAXN 100010 using namespace std; int n,m,t; int head[MAXN<<1],done[MAXN],dis[MAXN],dp[MAXN]; struct Node { int u,d; friend bool operator < (Node x,Node y) {return x.d>y.d;} }; struct Edge{int nxt,to,dis;}edge[MAXN<<1]; struct Line{int u,v,w;}line[MAXN<<1]; inline void add(int from,int to,int dis) { edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t; } inline void dij(int x) { priority_queue<Node>q; memset(done,0,sizeof(done)); memset(dis,0x3f,sizeof(dis)); q.push((Node){x,0}); dis[x]=0; while(!q.empty()) { int u=q.top().u;q.pop(); if(done[u]) continue; done[u]=1; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(dis[v]>dis[u]+edge[i].dis) dis[v]=dis[u]+edge[i].dis,q.push((Node){v,dis[v]}); } } } inline int solve(int x) { if(x==2) return dp[x]=1; int cur_ans=0; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(!dp[v]) dp[v]=solve(v); cur_ans+=dp[v]; } return cur_ans; } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(scanf("%d%d",&n,&m)==2&&n) { memset(head,0,sizeof(head)); memset(dp,0,sizeof(dp)); t=0; for(int i=1;i<=m;i++) { scanf("%d%d%d",&line[i].u,&line[i].v,&line[i].w); add(line[i].u,line[i].v,line[i].w); add(line[i].v,line[i].u,line[i].w); } dij(2); // for(int i=1;i<=n;i++) printf("dis[%d]=%d\n",i,dis[i]); memset(head,0,sizeof(head)); t=0; for(int i=1;i<=m;i++) { if(dis[line[i].u]>dis[line[i].v]) add(line[i].u,line[i].v,line[i].w); if(dis[line[i].v]>dis[line[i].u]) add(line[i].v,line[i].u,line[i].w); } printf("%d\n",solve(1)); } return 0; }
最短路樹:用dij算法能夠求出單元最短路樹,方法是在發現\(d[i]+w(i,j)<d[j]\)時除了更新\(d[j]\)以外還要設置\(p[j]=i\)。這樣,全部點就造成了一棵樹。
要從起點出發沿着最短路走到其餘任意點,只須要順着樹上的邊走便可。
給出一個n個節點m條邊的無向圖(n<=100,m<=1000),每條邊上有一個正權。令c等於每對節點的最短路長度之和。要求刪除一條邊後使得新的c值c'最大。不聯通的兩點的最短路長度視爲L。
在源點肯定的狀況下,只要最短路樹不被破壞,起點到全部點的距離都不會發生改變。換句話說,只有刪除最短路樹上的n-1條邊(中的任意條),最短路樹才須要從新計算。
這樣的話,咱們對於每一個源點,最多隻須要求n次而不是m次最短路,時間複雜度爲\(O(n^2mlogn)\)(dij算法的時間複雜度爲\(O(mlogn)\))
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #define MAXN 2010 using namespace std; int n,m,l,t=1; long long ans1,ans2; int head[MAXN<<1],pre[MAXN],ex[MAXN<<1][MAXN],done[MAXN]; long long dis[MAXN],dist[MAXN]; struct Node { int u;long long d; friend bool operator < (Node x,Node y) {return x.d>y.d;} }; struct Edge{int nxt,to,dis;}edge[MAXN<<1]; inline void add(int from,int to,long long dis) { edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t; edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=dis,head[to]=t; } inline long long dij(int x,int op) { priority_queue<Node>q; memset(dis,127,sizeof(dis)); memset(done,0,sizeof(done)); memset(pre,-1,sizeof(pre)); q.push((Node){x,0}); dis[x]=0; while(!q.empty()) { int u=q.top().u;q.pop(); if(done[u]) continue; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if((i==op)||(i^1)==op) continue; if(dis[v]>dis[u]+edge[i].dis) { dis[v]=dis[u]+edge[i].dis; q.push((Node){v,dis[v]}); pre[v]=i; } } } for(int i=1;i<=n;i++) if(pre[i]!=-1&&op==0) ex[pre[i]][x]=ex[pre[i]^1][x]=1; long long cur_ans=0; for(int i=1;i<=n;i++) { if(dis[i]<1e17) cur_ans+=dis[i]; else cur_ans+=l; } return cur_ans; } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); freopen("ce.out","w",stdout); #endif while(scanf("%d%d%d",&n,&m,&l)==3) { memset(head,0,sizeof(head)); memset(ex,0,sizeof(ex)); t=1; ans1=0,ans2=0; for(int i=1;i<=m;i++) { int x,y;long long w; scanf("%d%d%lld",&x,&y,&w); add(x,y,w); } for(int i=1;i<=n;i++) dist[i]=dij(i,0),ans1+=dist[i]; for(int i=2;i<=t;i+=2) { long long cur_ans=0; for(int j=1;j<=n;j++) { if(ex[i][j]) cur_ans+=dij(j,i); else cur_ans+=dist[j]; } ans2=max(ans2,cur_ans); } printf("%lld %lld\n",ans1,ans2); } return 0; }
運送貨物須要繳納過路費。進入一個尋裝須要繳納一個單位的貨物,進入一個城鎮時,每20個單位的貨物中就要上繳1個單位(好比,攜帶70個單位的貨物進入城鎮,須要繳納4個單位的貨物)。如今給定你一張圖,請找出一條繳納過路費最少的路線,並輸出。若是有多條路線,輸出字典序最小的。
逆推的dij算法。令\(d[i]\)表示進入節點i以後,至少須要\(d[i]\)個單位的貨物,到達目的地時貨物數量才足夠。則每次選擇一個\(d[i]\)最小的未標號的節點,更新它的全部前驅節點的d值就好了。輸出路徑的時候根據d值就能夠構造出字典序最小的解。
emmmm爲何我把書上的解釋全抄下來了......由於我以爲寫得確實比較清晰啊qwq
這個題個人代碼仍是不知道爲何鍋了。。。先敷衍一下放個maomao的吧......
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 200 + 5; const int INF = 0x3f3f3f3f3f3f3f3f; vector <int> G[N]; int n, p, kase, dis[N], done[N]; char s, t; int get_tot (int w) { int l = 0, r = 1e17; while (l < r) { int mid = (l + r) >> 1; if (mid - ceil ((1.0 * mid) / 20.0) >= w) { r = mid; } else { l = mid + 1; } } return r; } struct HeapNode { int u, d; bool operator < (HeapNode rhs) const { return d > rhs.d; } }; priority_queue <HeapNode> q; void solve () { memset (done, 0, sizeof (done)); memset (dis, 0x3f, sizeof (dis)); dis[(int)t] = isupper (t) ? get_tot (p) : p + 1; q.push ((HeapNode) {t, dis[(int)t]}); while (!q.empty ()) { HeapNode now = q.top (); q.pop (); if (done[now.u]) continue; for (int i = 0; i < (int) G[now.u].size (); ++i) { int v = G[now.u][i]; int _w = isupper (v) ? get_tot (dis[now.u]) : dis[now.u] + 1; if (dis[v] > _w) { dis[v] = _w; q.push ((HeapNode) {v, dis[v]}); } } done[now.u] = true; } } bool cmp (int lhs, int rhs) { return dis[lhs] == dis[rhs] ? lhs < rhs : dis[lhs] < dis[rhs]; } signed main () { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while (cin >> n) { if (n == -1) break; cout << "Case " << ++kase << ":" << endl; for (int i = 0; i < N; ++i) G[i].clear (); for (int i = 1; i <= n; ++i) { static char u, v; cin >> u >> v; G[(int)u].push_back ((int)v); G[(int)v].push_back ((int)u); } cin >> p >> s >> t; solve (); int min_dis = 0x7fffffffffffffffll; for (int i = 0; i < (int)G[(int)s].size (); ++i) { int v = G[(int)s][i]; min_dis = min (min_dis, dis[v]); } for (int i = 0; i < N; ++i) { if (!G[i].empty ()) { sort (G[i].begin (), G[i].end (), cmp); } } if (s == t) { cout << p << endl; cout << (char) t << endl; continue; } cout << min_dis << endl; int now = s; cout << (char) s << "-"; while (now != t) { now = G[now][0]; cout << (char) now; if (now != t) cout << "-"; } cout << endl; } }
給定一個n個點m條邊(n<=50)的加權有向圖,求平均權值最小的迴路。輸入沒有自環。
二分答案。假設存在一個包含k條邊的迴路,迴路上各條邊的權值爲\(w_1,w_2,...,w_k\),那麼平均值小於mid意味着\((w_1-mid)+(w_2-mid)+...+(w_k-mid)<0\),那麼就直接給每條邊減去mid,而後用SPFA判斷一下是否有負環就好了。
注意小數二分的時候精度不要取過高......常數太大會T的.......
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #define MAXN 110 #define eps 1e-7 using namespace std; int n,m,t,T,kase; int head[MAXN],done[MAXN],cnt[MAXN]; double dis[MAXN]; struct Line{int u,v;double w;}line[MAXN*MAXN]; struct Edge{int nxt,to;double dis;}edge[MAXN*MAXN]; inline void add(int from,int to,double dis) { edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t; } inline bool spfa(int x) { queue<int>q; for(int i=0;i<=n;i++) dis[i]=1e9,done[i]=cnt[i]=0; q.push(x);done[x]=1;dis[x]=0; while(!q.empty()) { int u=q.front();q.pop();done[u]=0; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(dis[v]>dis[u]+edge[i].dis) { dis[v]=dis[u]+edge[i].dis; if(!done[v]) { done[v]=1; q.push(v); if(++cnt[v]>=n) return false; } } } } return true; } inline bool check(double x) { bool flag=false; for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].nxt) edge[j].dis-=x; for(int i=1;i<=n;i++) if(!spfa(i)==true) flag=true; for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].nxt) edge[j].dis+=x; // puts("oh no"); return flag; } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif scanf("%d",&T); while(T--) { memset(head,0,sizeof(head)); t=0; scanf("%d%d",&n,&m); double l=1e9,r=0.0; for(int i=1;i<=m;i++) { scanf("%d%d%lf",&line[i].u,&line[i].v,&line[i].w); add(line[i].u,line[i].v,line[i].w); r=max(r,line[i].w); l=min(l,line[i].w); } printf("Case #%d: ",++kase); if(!check(r+1)) printf("No cycle found.\n"); else { while(l+eps<r) { double mid=(l+r)/2.0; // printf("l=%.2lf r=%.2lf mid=%.2lf\n",l,r,mid); if(check(mid)) r=mid; else l=mid; } printf("%.2lf\n",r); } } return 0; }
給定一個有向圖,每條邊都有一個權值。每次你能夠選擇一個節點v和一個整數d,把全部以v爲終點的邊的權值減少d,把全部以v爲起點的邊的權值增長d,最後要讓全部邊權的最小值爲正且儘可能大。
由於不一樣的操做互不影響,因此能夠按照任意順序實施這些操做。另外,對於同一個節點的屢次操做也能夠合併,因此咱們能夠令sum(u)表示做用於節點u之上的全部d之和。而後二分答案x,問題轉化成爲是否可讓全部操做完成後每條邊的權值均不小於x。對於邊a->b,操做完以後應該是\(w(a,b)+sum[a]-sum[b] \ge x\),也就是\(sum[b]-sum[a]<=w(a,b)-x\)。這樣,就是一個差分約束系統了,咱們連a到b,權值爲\(w(a,b)-x\)的邊。而後用負環來判斷這個差分約束系統是否有解。
對了,書上的翻譯感受有問題。依AC代碼來看,應該是讓全部邊的最小值爲正且儘可能大......
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define MAXN 510 using namespace std; int n,m,t; int head[MAXN],done[MAXN],dis[MAXN],cnt[MAXN]; struct Edge{int nxt,to,dis;}edge[3000]; struct Line{int u,v,w;}line[3000]; inline void add(int from,int to,int dis) { edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t; } inline bool spfa() { queue<int>q; for(int i=1;i<=n;i++) q.push(i),done[i]=1,dis[i]=0x3f3f3f3f,cnt[i]=0; while(!q.empty()) { int u=q.front();q.pop();done[u]=0; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(dis[v]>dis[u]+edge[i].dis) { dis[v]=dis[u]+edge[i].dis; if(!done[v]) { done[v]=1; cnt[v]++; q.push(v); if(cnt[v]>=n) return false; } } } } return true; } inline bool check(int x) { bool flag=false; for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].nxt) edge[j].dis-=x; if(spfa()) flag=true; for(int i=1;i<=n;i++) for(int j=head[i];j;j=edge[j].nxt) edge[j].dis+=x; return flag; } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(scanf("%d%d",&n,&m)!=EOF) { memset(head,0,sizeof(head)); t=0; int l=0,r=-0x3f3f3f3f,ans; for(int i=1;i<=m;i++) { scanf("%d%d%d",&line[i].u,&line[i].v,&line[i].w); add(line[i].u,line[i].v,line[i].w); r=max(r,line[i].w); } if(!check(1)) {printf("No Solution\n");continue;} else if(check(r)) {printf("Infinite\n");continue;} while(l<=r) { int mid=(l+r)>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%d\n",ans); } return 0; }
翻譯仍是去看書吧.......(反正我大概也是每次都抄一遍)
拆點的最短路,由於一個位置表示的狀態不同,因此咱們要拆點來分別表明這些狀態
把每一個點\((r,c)\)拆成8個點\((r,c,dir,double)\),分別表示上一步從上下左右的哪一個方向(dir)移動到這個點,以及移動到這個點的這條邊的權值是否已經加倍(doubled)
題解也仍是去看書吧.......(反正圖我也是畫不出來的......)
#include<iostream> #include<cmath> #include<cstring> #include<algorithm> #include<cstdio> #include<queue> #include<vector> #define MAXN 100010 #define INF 0x3f3f3f3f using namespace std; inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48); ch=getchar();} return x; } int n,R,C,r1,c1,r2,c2,kase,t; int inv[4]={2,3,0,1},dr[4]={-1,0,1,0},dc[4]={0,-1,0,1}; int grid[110][110][4],id[110][110][4][2]; int dis[MAXN],done[MAXN],head[MAXN]; const int Up=0; const int Left=1; const int Down=2; const int Right=3; struct Edge{int nxt,to,dis;}edge[20000010]; struct Node { int u,d; friend bool operator < (struct Node x,struct Node y) {return x.d>y.d;} }; inline void add(int from,int to,int dis) { // printf("[%d %d] %d\n",from,to,dis); edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t; } inline int ID(int r,int c,int dir,int doubled) { int &x=id[r][c][dir][doubled]; if(x==0) x=++n; return x; } inline bool cango(int r,int c,int dir) { if(r<1||r>R||c<1||c>C) return false; return grid[r][c][dir]>0; } inline void dij(int s) { priority_queue<Node>q; memset(done,0,sizeof(done)); memset(dis,0x3f,sizeof(dis)); dis[s]=0;q.push((Node){s,0}); while(!q.empty()) { int u=q.top().u;q.pop(); if(done[u]) continue; done[u]=1; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(dis[v]>dis[u]+edge[i].dis) { dis[v]=dis[u]+edge[i].dis; q.push((Node){v,dis[v]}); } } } } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(scanf("%d%d%d%d%d%d",&R,&C,&r1,&c1,&r2,&c2)==6) { if(R==0&&C==0) break; memset(head,0,sizeof(head)); t=0; for(int r=1;r<=R;r++) { for(int c=1;c<=C-1;c++) grid[r][c][Right]=grid[r][c+1][Left]=read(); if(r!=R) for(int c=1;c<=C;c++) grid[r][c][Down]=grid[r+1][c][Up]=read(); } n=0; memset(id,0,sizeof(id)); for(int dir=0;dir<4;dir++) if(cango(r1,c1,dir)) add(0,ID(r1+dr[dir],c1+dc[dir],dir,1),grid[r1][c1][dir]*2); for(int r=1;r<=R;r++) for(int c=1;c<=C;c++) for(int dir=0;dir<4;dir++) if(cango(r,c,inv[dir])) for(int newdir=0;newdir<4;newdir++) if(cango(r,c,newdir)) for(int doubled=0;doubled<2;doubled++) { int newr=r+dr[newdir]; int newc=c+dc[newdir]; int v=grid[r][c][newdir],newdoubled=0; if(dir!=newdir) { if(!doubled) v+=grid[r][c][inv[dir]]; newdoubled=1; v+=grid[r][c][newdir]; } add(ID(r,c,dir,doubled),ID(newr,newc,newdir,newdoubled),v); } dij(0); int ans=INF; for(int dir=0;dir<4;dir++) if(cango(r2,c2,inv[dir])) for(int doubled=0;doubled<2;doubled++) { int v=dis[ID(r2,c2,dir,doubled)]; if(!doubled) v+=grid[r2][c2][inv[dir]]; ans=min(ans,v); } printf("Case %d: ",++kase); if(ans==INF) printf("Impossible\n"); else printf("%d\n",ans); } return 0; }
給你一些票,每張聯票上標明上面以此通過的站,以及本票的價錢。必須從頭開始坐,能夠在中途任何一站下飛機,下飛機後票上繳,不能夠再次使用本票。
如今給出一些行程單,問如何買票能使得總花費最小(同一種票可以買多張)。輸入保證行程老是可行的,行程單上的城市必須按照順序到達,可是中間能夠通過一些輔助城市。
輸入保證每組數據最多包含20種聯票和20個行程單,聯票或者行程單上的相鄰城市保證不一樣。票和行程單都從1開始編號。
#include<cstdio> #include<cstring> #include<vector> #include<queue> #include<map> #include<stack> #include<algorithm> using namespace std; const int maxn=4100; const int inf=1e9; struct HeapNode { int d,u; bool operator<(const HeapNode& rhs)const {return d>rhs.d;} }; struct Edge{int from,to,dist,id;}; struct Dijkstra { int n,m; vector<Edge>edges; vector<int>G[maxn]; bool done[maxn]; int d[maxn],p[maxn]; void init(int n) { this->n=n; for(int i=0;i<n;i++)G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int dist,int id) { edges.push_back((Edge){from,to,dist,id}); m=edges.size(); G[from].push_back(m-1); } void dij(int s) { priority_queue<HeapNode>Q; for(int i=1;i<=n;i++)d[i]=inf; d[s]=0; memset(done,0,sizeof(done)); Q.push((HeapNode){0,s}); while(!Q.empty()) { HeapNode x=Q.top();Q.pop(); int u=x.u; if(done[u])continue; done[u]=true; for(int i=0;i<G[u].size();i++) { Edge& e=edges[G[u][i]]; if(d[e.to]>d[u]+e.dist) { d[e.to]=d[u]+e.dist; p[e.to]=G[u][i]; Q.push((HeapNode){d[e.to],e.to}); } } } } void print(int s,int t) { stack<int>stk; while(t!=s) { stk.push(edges[p[t]].id); t=edges[p[t]].from; } while(!stk.empty()) { printf(" %d",stk.top()); stk.pop(); } printf("\n"); } }solver; int id[12][410],res,tot; vector<int>ticket[21]; map<int,int>mp; inline int ID(int x) { if(!mp.count(x)) mp[x]=++tot; return mp[x]; } inline int ID2(int x,int y) { if(id[x][y]==0) id[x][y]=++res; return id[x][y]; } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif int n,m,q,u,v,money,kase=0; while(~scanf("%d",&n)&&n) { int cost[21]; mp.clear(); tot=0; for(int i=1;i<=n;i++) { ticket[i].clear(); scanf("%d%d",&cost[i],&m); for(int j=1;j<=m;j++) { scanf("%d",&u); ticket[i].push_back(ID(u)); } } kase++; scanf("%d",&q); vector<int>line; for(int p=1;p<=q;p++) { int num; line.clear(); memset(id,0,sizeof(id)); res=0; scanf("%d",&num); for(int i=1;i<=num;i++) { scanf("%d",&u); line.push_back(ID(u)); } solver.init(num*tot+1); for(int tic=1;tic<=n;tic++) for(int pos=1;pos<num;pos++) { int next=pos; for(int i=1;i<ticket[tic].size();i++) { if(ticket[tic][i]==line[next])next++; solver.AddEdge(ID2(pos,ticket[tic][0]),ID2(next,ticket[tic][i]),cost[tic],tic); if(next==num) break; } } solver.dij(ID2(1,line[0])); int ans=solver.d[ID2(num,line[num-1])]; printf("Case %d, Trip %d: Cost = %d\n",kase,p,ans); printf(" Tickets used:"); solver.print(ID2(1,line[0]),ID2(num,line[num-1])); } } }
平面圖轉對偶圖,用最短路求最小割。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define S 0 #define T tot+1 #define MAXN 5000010 using namespace std; int n,m,t,cur,kase,tot; int dis[MAXN],done[MAXN],head[MAXN],id[1010][1010][2]; struct Node { int u,d; friend bool operator <(struct Node x,struct Node y) {return x.d>y.d;} }; struct Edge{int nxt,to,dis;}edge[MAXN<<1]; inline void add(int from,int to,int dis) { // printf("[%d %d] %d\n",from,to,dis); edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t; edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=dis,head[to]=t; } inline void dij() { priority_queue<Node>q; memset(dis,0x3f,sizeof(dis)); memset(done,0,sizeof(done)); q.push((Node){S,0});dis[S]=0; while(!q.empty()) { int u=q.top().u; q.pop(); if(done[u]) continue; done[u]=1; for(int i=head[u];i;i=edge[i].nxt) { int v=edge[i].to; if(dis[u]+edge[i].dis<dis[v]) dis[v]=dis[u]+edge[i].dis,q.push((Node){v,dis[v]}); } } } int main() { #ifndef ONLINE_JUDGE freopen("ce.in","r",stdin); #endif while(scanf("%d%d",&n,&m)==2&&n+m) { memset(head,0,sizeof(head)); t=tot=0; n--,m--; // printf("n=%d m=%d\n",n,m); for(int i=0;i<=n+1;i++) for(int j=0;j<=m+1;j++) for(int k=0;k<=1;k++) id[i][j][k]=++tot; for(int i=1;i<=n+1;i++) { int x; for(int j=1;j<=m;j++) { scanf("%d",&x); add(id[i][j][1],id[i-1][j][0],x); } } for(int i=1;i<=n;i++) { int x; for(int j=1;j<=m+1;j++) { scanf("%d",&x); add(id[i][j][0],id[i][j-1][1],x); } } for(int i=1;i<=n;i++) { int x; for(int j=1;j<=m;j++) { scanf("%d",&x); add(id[i][j][1],id[i][j][0],x); } } for(int i=1;i<=n;i++) add(S,id[i][0][1],0); for(int j=1;j<=m;j++) add(S,id[n+1][j][1],0); for(int j=1;j<=m;j++) add(id[0][j][0],T,0); for(int i=1;i<=n;i++) add(id[i][m+1][0],T,0); dij(); printf("Case %d: Minimum = %d\n",++kase,dis[T]); // printf("%d\n",dis[T]); } return 0; }