ACM一类方程问题的求解[最短路建模] bzoj2118
在ACM生涯里已经预见两回判断这种方程是否有解、有几个解的问题了。
例如:
1
给定非负整数a,b,c,n,请判断ax+by+cz=n是否存在(x,y,z)均为非负整数的解
题目链接:http://oj.xjtuacm.com/contest/14/problem/124/
再例如:
2
现有方程A1 * X1 + A2 * X2 + ... + An * Xn = P
A1, A2, ... , An为变量X1, X2, ... , Xn的系数
给定P的取值范围,求有多少个P使得方程存在非负整数解
题目链接:http://oj.xjtuacm.com/contest/14/problem/124/
而这种方程看似是数论问题往往能通过建模的方法转换成为图论里面最短路问题,很奇妙,对吧!
我们就拿题目2来说,设所有的给出的并且满足这个方程的P值,如果我们把P值对Ai进行取模,得到的数必然存在于Ai的剩余系中,也就是说
P%Ai 在区间[0,Ai-1]里面,乍一看,我们这样做岂不是把信息减损了?P映射到[0,Ai-1]相当于把模数相同的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意义下)
但是这里是有一个坑点的,细心的朋友可以发现,我上面举得那个例子实际上是错误的,因为这样扩展y的方法会造成P的数量比真实情况下要多。
这样理解:
假设满足mod Ai = 2意义下的P只有7的话,那么根据上面的方法将会的得到一个假的P(P = 2),相当于无形之间把答案的数量增多了。
这就要求我们,保存一个最小的P(mod Ai = 2),在最小的这个P的基础上开始扩展(而不是从2的基础上开始扩展),就可以保证答案的正确性了。
由于这个Ai是可以随便取得,但是取最小的那个是最好的,因为取最小的那个Ai,剩余系是最小的,我们记A = min{Ai}
这样我们的问题就转化成为求所有对应A的剩余系中值的最小P,求出来这个以后直接进行扩展就好了。
而要求这样的P,我们就可以采用最短路的做法,具体怎么做呢?
我们把点定义成为剩余系中的值,把边定义成为,一个剩余系中的值向另一个剩余系中的值转移所需要的代价。
很明显的,d[0] = 0
然后从i出发,向(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相关推荐
- 割线法求解过程_潮流方程的割线法求解
潮流方程的割线法求解 赵 泽,何飞跃 [摘 要] 摘要:电力系统潮流分析是研究电力系统稳定的重要手段,通过数值 仿真的方法把电力系统的详细运行状态呈现给运行人员,以便了解给定条件下 电力系统的稳定运行 ...
- matlab lyap,Matlab的Lyapunov、Sylvester和Riccati方程的Matlab求解
一.连续Lyapunov方程连续Lyapunov方程可以表示为 Lyapunov方程来源与微分方程稳定性理论,其中要求C为对称正定的n×n方阵,从而可以证明解X亦为n×n对称矩阵,这类方程直接求解比较 ...
- lyapunov函数 matlab,Lyapunov、Sylvester和Riccati方程的Matlab求解
Lyapunov.Sylvester和Riccati方程是控制系统常用到的几个方程,应用和计算比较广泛 一.Lyapunov方程 1.连续Lyapunov方程连续Lyapunov方程可以表示为 Lya ...
- lyapunov函数 matlab,科学网-[转载]Matlab的Lyapunov、Sylvester和Riccati方程的Matlab求解-吴雄君的博文...
一.连续Lyapunov方程连续Lyapunov方程可以表示为 Lyapunov方程来源与微分方程稳定性理论,其中要求C为对称正定的n×n方阵,从而可以证明解X亦为n×n对称矩阵,这类方程直接求解比较 ...
- 用计算机算方程近似值,借助CASIO图形计算器探索方程近似解的求解
借助 CASIO 图形计算器探索方程近似解的求解 高建彪 (广东省中山市东升高中) 摘要:高中数学新课程标准新增了用二分法求方程近似解这一内容,重在初步理解 二分法的原理, 若要深入透彻地研究方程近似 ...
- 【MPC的前身方法二】(5.3)机器人动力学模型+反馈控制+齐次线性方程AX=B求解反作用力并优化方法
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录 前言 一.建立单刚体动力学模型(近似抽象模型) 二.建立单刚体模型的运动方 ...
- JAVA:实现A-Star求解最短路最有效的算法(附完整源码)
JAVA:实现A-Star求解最短路最有效的算法 /*Time Complexity = O(E), where E is equal to the number of edges*/ package ...
- 二维有限元方程matlab,有限元法求解二维Poisson方程的MATLAB实现
有限元法求解二维 Poisson 方程的 MATLAB 实现 陈 莲a ,郭元辉b ,邹叶童a ( 西华师范大学 a. 数学与信息学院; b. 教育信息技术中心,四川南充 6437009) 摘 要: ...
- matlab偏导数方程,[转载]Matlab求解微分方程(2)——偏微分方程的求解
从写完上一篇常微分方程的求解到现在已经很长时间了,这周也一直忙于报到的各种事宜,无暇坐下来写些东西,趁着这个周末,终于完成了这个姊妹篇. 对于偏微分方程的求解,Matlab提供了两种工具.第一种是pd ...
最新文章
- W3C HTML 工作组联合主席Paul Cotton谈HTML5发展愿景
- 架构师书单 2nd Edition--转载
- python算法与数据结构-冒泡排序算法
- JAVA入门级教学之(classpath的配置)
- weblogic 64位 linux,weblogic在linux和window下的安装
- Management reporter 2012 与AX 2012
- Python判断文件和文件夹是否存在的方法
- PHP实现的服务器端,PHP实现服务器端允许客户端ajax跨域
- 4. 两个排序数组的中位数
- 计算机组成原理统一试卷,计算机组成原理试卷(含答案).doc
- ATX电源的工作原理
- 牛顿迭代法(求平方根)
- shim是什么?Vue响应式原理
- PayPal 如何付款
- 关于SDRAM存储器
- 酷鲨商城后台管理界面
- 粤行大湾区,探寻中国科技前沿
- 交换机 POE 学习
- python中seth是什么意思_python中的seth有什么用
- jpg转pdf转换器注册码使用
热门文章
- neo4j 查询同一节点的两个上级_WhatRoute for Mac(互联网流量诊断查询工具)
- 数据结构——最小生成树之克鲁斯卡尔算法(Kruskal)
- Linux系统语言教程,Linux操作系统基础及语言基础教程-麦可网张凌华
- 81. 搜索旋转排序数组 II(014)二分查找+思路+详解+二种做法
- [PAT乙级]1011 A+B 和 C
- C++ class类 实现搜索二叉树(BST)
- 洛谷 P1162 填涂颜色-dfs染色法
- android 按钮 菜单,(期末要考啊)Android的menu(菜单)按钮的使用
- Linux下Java环境变量配置
- ubuntu 16.04 安装 python2.7 以及 cv2, dist-package 和 site-package 的区别, import cv2 出问题解答