日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ACM一类方程问题的求解[最短路建模] bzoj2118

發(fā)布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ACM一类方程问题的求解[最短路建模] bzoj2118 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在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 <algorithm> using namespace std; typedef long long int LL; LL extgcd(LL a,LL b,LL& x,LL& y) {LL d = a;if(b != 0){d = extgcd(b,a%b,y,x);y -= (a/b)*x;}else{x = 1;y = 0;}return d; } void solve(){LL k[3],n;int cas = 0;while(~scanf("%lld%lld%lld%lld",&k[0],&k[1],&k[2],&n)){sort(k,k+3);LL limit = min(n/k[2],k[0]);int f = 0;if(n%k[2] != 0)for(int i = 0;i <= limit;i++){LL x,y;LL res = n - (LL)i*k[2];LL d = extgcd(k[0],k[1],x,y);if(res % d != 0){continue;}else{if(x >=0 && y >= 0){f = 1;break;}else {if(y < 0){swap(x,y);swap(k[0],k[1]);} LL r = (res%k[1])*(-x)%k[1];if(r == 0){f = 1;break;}else{//cout<<double(n)<<endl;if(res >= 1000000){if(res/k[0]*y >= res/k[1]*(-x)){f = 1;break;}}elseif(res*y/k[0] >= res*(-x)/k[1]+1){f = 1;break;}}}}}else{f = 1;}printf("Case #%d: ",++cas);if(f){puts("Yes");}else{puts("No");}} } int main(){solve();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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。