ACM一类方程问题的求解[最短路建模] bzoj2118
在ACM生涯里已經預見兩回判斷這種方程是否有解、有幾個解的問題了。
例如:
1
給定非負整數(shù)a,b,c,n,請判斷ax+by+cz=n是否存在(x,y,z)均為非負整數(shù)的解
題目鏈接:http://oj.xjtuacm.com/contest/14/problem/124/
再例如:
2
現(xiàn)有方程A1 * X1 + A2 * X2 + ... + An * Xn = P
A1, A2, ... , An為變量X1, X2, ... , Xn的系數(shù)
給定P的取值范圍,求有多少個P使得方程存在非負整數(shù)解
題目鏈接:http://oj.xjtuacm.com/contest/14/problem/124/
而這種方程看似是數(shù)論問題往往能通過建模的方法轉換成為圖論里面最短路問題,很奇妙,對吧!
我們就拿題目2來說,設所有的給出的并且滿足這個方程的P值,如果我們把P值對Ai進行取模,得到的數(shù)必然存在于Ai的剩余系中,也就是說
P%Ai 在區(qū)間[0,Ai-1]里面,乍一看,我們這樣做豈不是把信息減損了?P映射到[0,Ai-1]相當于把模數(shù)相同的P都給去沖了。不用擔心,我們有神奇的辦法可以從剩余系[0,Ai-1]
中把所有的P無損的還原回來。
那么怎么做呢?
假設我們已經有了所有滿足
(A1 * X1 + A2 * X2 + ... + An * Xn)%Ai = P%(Ai) = y
的y值,那么我們將y不斷地增加Ai,然后判斷得到的值是否在[Pmin,Pmax],是不是就可以得到所有的P值了呢。(哇,真的很奇妙!)
舉個例子來說,
我們選取Ai等于5?
Pmin = 0,Pmax = 11,其中一個y = 2
那么2,2+5 = 7就是滿足條件的所有P(%Ai = 2意義下)
但是這里是有一個坑點的,細心的朋友可以發(fā)現(xiàn),我上面舉得那個例子實際上是錯誤的,因為這樣擴展y的方法會造成P的數(shù)量比真實情況下要多。
這樣理解:
假設滿足mod Ai = 2意義下的P只有7的話,那么根據(jù)上面的方法將會的得到一個假的P(P = 2),相當于無形之間把答案的數(shù)量增多了。
這就要求我們,保存一個最小的P(mod Ai = 2),在最小的這個P的基礎上開始擴展(而不是從2的基礎上開始擴展),就可以保證答案的正確性了。
由于這個Ai是可以隨便取得,但是取最小的那個是最好的,因為取最小的那個Ai,剩余系是最小的,我們記A = min{Ai}
這樣我們的問題就轉化成為求所有對應A的剩余系中值的最小P,求出來這個以后直接進行擴展就好了。
而要求這樣的P,我們就可以采用最短路的做法,具體怎么做呢?
我們把點定義成為剩余系中的值,把邊定義成為,一個剩余系中的值向另一個剩余系中的值轉移所需要的代價。
很明顯的,d[0] = 0
然后從i出發(fā),向(i+Ak)%A這個點進行轉移的邊的代價為Ai
也就是說我們可以把i和(i+Ak)做一條邊權位Ak得邊,
圖建好以后,做一個dijkstra就好了
得到的d[i]就是對應剩余系中的i的P的最小值。
第一題和第二題是基本相同的,這里就不贅述了(值得一提的是,第一題還可以用擴展歐幾里得來做)
代碼:
第一題(最短路建模的方法)
#include <iostream> #include <queue> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int a,b,c; long long n; const int MAXN = 3e5; const int V_MAXN = 2e5; const long long INF = 1e18; int N,X,Y,MAX; int head[V_MAXN]; struct edge{ int v; int next; int cost; }Es[MAXN<<1]; long long d[MAXN]; int cnt; typedef pair<int,int> P; void dijkstra(int x){ for(int i = 0;i < MAXN;i++) d[i] = INF; d[x] = 0; priority_queue<P,vector<P>,greater<P> > que; que.push(P(0,x)); while(!que.empty()){ P p = que.top();que.pop(); int dis = p.first; int v = p.second; if(d[v] < dis) continue; //for(int i = 0;i < 2*N+2;i++){ for(int e = head[v];e!= -1;e = Es[e].next){ int cost = Es[e].cost; int i = Es[e].v; if(cost + d[v] < d[i]){ d[i] = d[v] + cost; que.push(P(d[i],i)); } } } } inline void add_edge(int i,int j,int cost){ //G[i][j] = cost; Es[cnt].v = j; Es[cnt].cost = cost; Es[cnt].next = head[i]; head[i] = cnt++; } void init(){ cnt = 0; memset(head,-1,sizeof(head)); } int main(){int cas = 0;while(~scanf("%d%d%d%lld",&a,&b,&c,&n)){init();int p = min(a,b);p = min(p,c);for(int i = 0;i < p;i++){add_edge(i,(i+a)%p,a);add_edge(i,(i+b)%p,b);add_edge(i,(i+c)%p,c);}dijkstra(0);for(int i = 0;i < p;i++){if(d[i] <= n){if((n - d[i])%p == 0){printf("Case #%d: Yes\n",++cas);goto ns;}}}printf("Case #%d: No\n",++cas);ns:;} return 0; }第一題(擴展歐幾里得地方法)
第二題(最短路建模的方法)
#include <iostream> #include <cstdio> #include <vector> #include <algorithm> #include <queue> #include <cstring> using namespace std; #define int long long const int MAXN = 1e7; const int V_MAXN = 1e7; const int INF = 1e18; int N,X,Y,MAX; int head[V_MAXN]; struct edge{ int v; int next; int cost; }Es[MAXN<<1]; int d[MAXN]; int cnt; typedef pair<int,int> P; void dijkstra(int x){ for(int i = 0;i < MAXN;i++) d[i] = INF; d[x] = 0; priority_queue<P,vector<P>,greater<P> > que; que.push(P(0,x)); while(!que.empty()){ P p = que.top();que.pop(); int dis = p.first; int v = p.second; if(d[v] < dis) continue; //for(int i = 0;i < 2*N+2;i++){ for(int e = head[v];e!= -1;e = Es[e].next){ int cost = Es[e].cost; int i = Es[e].v; if(cost + d[v] < d[i]){ d[i] = d[v] + cost; que.push(P(d[i],i)); } } } } inline void add_edge(int i,int j,int cost){ //G[i][j] = cost; Es[cnt].v = j; Es[cnt].cost = cost; Es[cnt].next = head[i]; head[i] = cnt++; } void init(){ cnt = 0; memset(head,-1,sizeof(head)); } int n; long long Pmin,Pmax; int A[20]; main(){while(scanf("%lld%lld%lld",&n,&Pmin,&Pmax) != EOF){init();int p = INF;for(int i = 1;i <= n;i++){scanf("%lld",&A[i]);if(A[i])p = min(p,A[i]);}if(p >= INF) {puts(Pmin == 0?"1":"0");continue;}for(int i = 0 ;i < p;i++){for(int j = 1;j <= n;j++){add_edge(i,(i + A[j])%p,A[j]);}}dijkstra(0);long long ans = 0;for(int i = 0;i < p;i++){if(d[i] < INF){long long l,r; if(d[i] < Pmin) {l = (Pmin - 1 - d[i])/p + 1;}else{l = 0;}if(d[i] <= Pmax){r = (Pmax - d[i])/p + 1; } else{r = 0;}ans += r - l;}}printf("%lld\n",ans);}return 0; }
總結
以上是生活随笔為你收集整理的ACM一类方程问题的求解[最短路建模] bzoj2118的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017西安交大ACM小学期 敏感词汇[
- 下一篇: Network of Schools P