求解差分约束系统,可以转化成图论的
单源最短路径(或最长路径)问题。
观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边(i,j),边权为bk。我们再增加一个源点s,s与所有定点相连,边权均为0。对这个图,以s为源点运行Bellman-ford算法(或SPFA算法),最终{d[ i]}即为一组可行解。
关键点: 搞清楚最后求的是什么
给定若干个连续的整数区间[ai, bi]以及ci,现在要求这样一个集合Z,集合Z中的元素与每个给定的区间的元素交集至少有ci个。(zoj)
S[i] = sigma(tj) 1<= j <= i 前缀和。 ti =1 ,表示i这个数出现了。
那么显然有
1) S[b] - S[a-1] >= c => S[a-1] - S[b] <= -c , 边(b , a-1 , -c)
2) 0 <= S[i] - S[i-1] <= 1
=> S[i]- S[i-1] <= 1 边 (i-1, i , 1)
=> S[i-1] - S[i] <= 0 边(i , i-1 , 0)
最关键的时候来了, 求什么?
区间[mi , mx] (出现数字的上下界) S[mx] - S[mi-1] >= M ,即求M。
=> S[mi-1] - S[mx] <= -M 边(mx , mi-1 , -M) 。 这个时候源点已经赤裸裸的出来了,mx 。
const int maxn = 50008 ;
struct Edge{
int v ;
int w ;
int next ;
}e[maxn<<2] ;
int g[maxn] , id ;
inline void add(int u , int v , int w){
e[id].v = v ;
e[id].w = w ;
e[id].next = g[u] ;
g[u] = id++ ;
}
queue<int> Q ;
bool in[maxn] ;
int dist[maxn] ;
void spfa(int s){
while(! Q.empty()) Q.pop() ;
memset(in , 0 , sizeof(in)) ;
in[s] = 1 ;
memset(dist , 63 , sizeof(dist)) ;
dist[s] = 0 ;
Q.push(s) ;
while(! Q.empty()){
int u = Q.front() ; Q.pop() ;
in[u] = 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ; int w = e[i].w ;
if(dist[u] + w < dist[v]){
dist[v] = dist[u] + w ;
if(! in[v]){
in[v] = 1 ;
Q.push(v) ;
}
}
}
}
}
int main(){
int n , i , mi , mx , a , b , c ;
while(cin>>n){
id = 0 ;
memset(g , -1 , sizeof(g)) ;
mi = maxn + 10 ;
mx = -1 ;
for(i = 1 ; i <= n ; i++){
scanf("%d%d%d" ,&a ,&b ,&c) ;
a++ ; b++ ;
add(b , a-1 , -c) ;
mi = min(a , mi) ;
mx = max(b , mx) ;
}
for(i = mi ; i <= mx ; i++){
add(i-1 , i , 1) ;
add(i , i-1 , 0) ;
}
spfa(mx) ; ;
printf("%d\n" , -dist[mi - 1]) ;
}
return 0 ;
}
给你一个N*M的矩阵C,问是否存在一个长度为N的数列a和长度为M的数列b使得所有C(i,j)*a(i)/b(j)在L , U范围内。
是否存在ai,bj,使得l<=cij*(ai/bj)<=u (1<=i<=n,1<=j<=m)成立(hdu)
把cij除到两边:l'<=ai/bj<=u',如果差分约束的话,应该是ai-bj的形式,于是可以取对数
log(l')<=log(ai)-log(bj)<=log(u')
把log(ai)和log(bj)看成两个点ai和bj,化成求最短路的形式:dis[ai]-dis[bj]<=log(u'),dis[bj]-dis[ai]<=-log(l')
然后判负环就行
注意的是,如果spfa队列判负环:
(1)不必判断某个点入队次数大于N,只要判断是否大于sqrt(1.0*N)
(2)或者所有点的入队次数大于T*N,即存在负环,一般T取2
N为所有点的个数
const int maxn = 1008 ;
struct Edge{
int v ;
double w ;
int next ;
}e[361000] ;
int g[maxn] , id ;
inline void add(int u , int v , double w){
e[id].v = v ;
e[id].w = w ;
e[id].next = g[u] ;
g[u] = id++ ;
}
int Q[361000] ;
bool in[maxn] ;
int relax[maxn] ;
double dist[maxn] ;
int spfa(int N){
int head = 0 , tail = -1 ;
for(int i = 1 ; i <= N ; i++){//加入虚拟源点
dist[i] = 0 ;
Q[++tail] = i ;
in[i] = 1 ;
relax[i] = 1 ;
}
int limit = (int)sqrt(0.5 + N) ;
while(head <= tail){
int u = Q[head++] ;
in[u] = 0 ;
if(relax[u] > limit) return 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ; ;
if(dist[u] + e[i].w < dist[v]){
dist[v] = dist[u] + e[i].w ;
if(! in[v]){
in[v] = 1 ;
relax[v]++ ;
Q[++tail] = v ;
}
}
}
}
return 1 ;
}
int main(){
int n , m , i , j ;
double L , U , x ;
while(scanf("%d%d%lf%lf" ,&n,&m,&L,&U) != EOF){
id = 0 ;
memset(g , -1 , (n+m+1)*sizeof(int)) ;
L = log(L) ;
U = log(U) ;
for(i = 1 ; i <= n ; i++){
for(j = 1 ; j <= m ; j++){
scanf("%lf" ,&x) ;
x = log(x) ;
add(n+j , i , U - x) ;
add(i , n+j , x - L) ;
}
}
if(spfa(n+m)) puts("YES") ;
else puts("NO") ;
}
return 0 ;
}
给出两种关于防御站位置的信息,一种是确切的信息,P A B X,表示a在b北面x距离的地方,另一种是V A B,表示只知道A在B的北面。问这些信息有没有矛盾。(poj)
操作P : d[A] -d[ B] = x
<=> d[A]-d[B] >= x 且d[A]-d[B] <= x
=> d[B] - d[A] <= -x 加边(A , B , -x) ;
d[A]-d[B] <= x 加边(B , A , x) ;
操作V : d[A] - d[B] >=1
=> d[B] - d[A] <= -1 加边(A, B, -1)
在spfa算法中,如果一个顶点入队列的次数超过n,则表示有向网中存在负权值回路。(《图论算法》)
const int maxn = 1008 ;
struct Edge{
int v ;
int w ;
int next ;
}e[400008] ;
int id , g[maxn] ;
inline void add(int u , int v , int w){
e[id].v = v ;
e[id].w = w ;
e[id].next = g[u] ;
g[u] = id++ ;
}
queue<int> Q ;
int relax[maxn] ;
bool in[maxn] ;
int dist[maxn] ;
int spfa(int s , int N){
while(! Q.empty()) Q.pop() ;
for(int i = 0 ; i <= N ; i++){
dist[i] = 100000000 ;
in[i] = 0 ;
relax[i] = 0 ;
}
dist[s] = 0 ;
Q.push(s) ;
while(! Q.empty()){
int u = Q.front() ; Q.pop() ;
in[u] = 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ;
if(dist[u] + e[i].w < dist[v]){
dist[v] = dist[u] + e[i].w ;
if(! in[v]){
in[v] = 1 ;
if(++relax[v] > N+1) return 0 ;
Q.push(v) ;
}
}
}
}
return 1 ;
}
int main(){
int i , n , m , a , b , c ;
char s[2] ;
while(cin>>n>>m){
memset(g , -1 , (n+1)*sizeof(int)) ;
id = 0 ;
while(m--){
scanf("%s" , s) ;
if(s[0] == 'P'){
scanf("%d%d%d" , &a , &b , &c) ;
add(b , a , c) ;
add(a , b , -c) ;
}
else{
scanf("%d%d" , &a , &b) ;
add(a , b , -1) ;
}
}
for(i = 1 ; i <= n ; i++) add(0 , i , 0) ;
if(spfa(0 , n)) puts("Reliable") ;
else puts("Unreliable") ;
}
return 0 ;
}
给出n个变量,m个约束公式 Sa + Sa+1 + .... + Sa+b < ki or > ki ,叫你判断是否存在着解满足这m组约束公式。(poj)
Sa + Sa+1 + .+ Sa+b = Sum[a+b] - Sum[a-1] . 注意加入源点n+1 。
const int maxn = 1008 ;
struct Edge{
int v ;
int w ;
int next ;
}e[400008] ;
int id , g[maxn] ;
inline void add(int u , int v , int w){
e[id].v = v ;
e[id].w = w ;
e[id].next = g[u] ;
g[u] = id++ ;
}
queue<int> Q ;
int relax[maxn] ;
bool in[maxn] ;
int dist[maxn] ;
int spfa(int s , int N){
while(! Q.empty()) Q.pop() ;
for(int i = 0 ; i <= N ; i++){
dist[i] = 100000000 ;
in[i] = 0 ;
relax[i] = 0 ;
}
dist[s] = 0 ;
Q.push(s) ;
while(! Q.empty()){
int u = Q.front() ; Q.pop() ;
in[u] = 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ;
if(dist[u] + e[i].w < dist[v]){
dist[v] = dist[u] + e[i].w ;
if(! in[v]){
in[v] = 1 ;
if(++relax[v] > N) return 0 ;
Q.push(v) ;
}
}
}
}
return 1 ;
}
int main(){
int i , n , m , a , b , k ;
char s[2] ;
while(cin>>n && n){
cin>>m ;
memset(g , -1 , (n+2)*sizeof(int)) ;
id = 0 ;
while(m--){
scanf("%d%d%s%d" , &a , &b , s , &k) ;
if(s[0] == 'l') add(a-1 , a+b , k-1) ;
else add(a+b , a-1 , -1-k) ;
}
for(i = 0 ; i <= n ; i++) add(n+1 , i , 0) ;
if(spfa(n+1 , n+2)) puts("lamentable kingdom") ;
else puts("successful conspiracy") ;
}
return 0 ;
}
A <= Xl + Xl+1 +...+ Xl+r <= B ,-10000<=xi<=10000输出字典序最大的解X 。(zoj)
图肯定连通,源点从0开始即可。
字典序最大的解。 X1 - X0(源点) >= M , 也就是这个M最大, Xi答案就是S[i] - S[i-1]
const int maxn = 1008 ;
struct Edge{
int v ;
int w ;
int next ;
}e[400008] ;
int id , g[maxn] ;
inline void add(int u , int v , int w){
e[id].v = v ;
e[id].w = w ;
e[id].next = g[u] ;
g[u] = id++ ;
}
queue<int> Q ;
int relax[maxn] ;
bool in[maxn] ;
int dist[maxn] ;
int spfa(int s , int N){
while(! Q.empty()) Q.pop() ;
for(int i = 0 ; i <= N ; i++){
dist[i] = 100000000 ;
in[i] = 0 ;
relax[i] = 0 ;
}
dist[s] = 0 ;
Q.push(s) ;
while(! Q.empty()){
int u = Q.front() ; Q.pop() ;
in[u] = 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ;
if(dist[u] + e[i].w < dist[v]){
dist[v] = dist[u] + e[i].w ;
if(! in[v]){
in[v] = 1 ;
if(++relax[v] > N) return 0 ;
Q.push(v) ;
}
}
}
}
return 1 ;
}
int main(){
int i , n , m , a , b , l , r , ok ;
char s[2] ;
while(cin>>n>>m){
memset(g , -1 , (n+2)*sizeof(int)) ;
id = 0 ;
ok = 0 ;
while(m--){
scanf("%d%d%d%d" , &l , &r , &a , &b) ;
add(r , l-1 , -a) ;
add(l-1 , r , b) ;
if(l > r) ok = 1 ;
}
if(ok){
puts("The spacecraft is broken!") ;
continue ;
}
for(i = 1 ; i <= n ; i++){
add(i-1 , i , 10000) ;
add(i, i-1 , 10000) ;
}
if(spfa(0 , n+1)){
printf("%d" , dist[1] - dist[0]) ;
for(i = 2 ; i <= n ; i++) printf(" %d" , dist[i] - dist[i-1]) ;
puts("") ;
}
else puts("The spacecraft is broken!") ;
}
return 0 ;
}
1必须站在0号位置, Pos[B] - Pos[A] >= C ,Pos[B] - Pos[A] <= C , i必须站在i-1的右边, n站的位置小于100000000 。求1-n 可站位置的区间。 fzu
最短路 求最大值
构造 d[i] - d[j] <= c 加边 (j , i , c)
最长路 求最小值
构造 d[i] - d[j] >=c 加边(j , i , c)
const int maxn = 1008 ;
struct Edge{
int v ;
int w ;
int next ;
}e[10008] , e2[10008] ;
int id , g[maxn] ;
inline void add(int u , int v , int w){
e[id].v = v ;
e[id].w = w ;
e[id].next = g[u] ;
g[u] = id++ ;
}
queue<int> Q ;
int relax[maxn] ;
bool in[maxn] ;
int dist[maxn] ;
int spfa(int s , int N){
while(! Q.empty()) Q.pop() ;
for(int i = 0 ; i <= N ; i++){
dist[i] = 1000000000 ;
in[i] = 0 ;
relax[i] = 0 ;
}
dist[s] = 0 ;
Q.push(s) ;
while(! Q.empty()){
int u = Q.front() ; Q.pop() ;
in[u] = 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ;
if(dist[u] + e[i].w < dist[v]){
dist[v] = dist[u] + e[i].w ;
if(! in[v]){
in[v] = 1 ;
if(++relax[v] > N) return 0 ;
Q.push(v) ;
}
}
}
}
return 1 ;
}
int spfa2(int s , int N){
while(! Q.empty()) Q.pop() ;
for(int i = 0 ; i <= N ; i++){
dist[i] = -1000000000 ;
in[i] = 0 ;
relax[i] = 0 ;
}
dist[s] = 0 ;
Q.push(s) ;
while(! Q.empty()){
int u = Q.front() ; Q.pop() ;
in[u] = 0 ;
for(int i = g[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ;
if(dist[u] + e[i].w > dist[v]){
dist[v] = dist[u] + e[i].w ;
if(! in[v]){
in[v] = 1 ;
if(++relax[v] > N) return 0 ;
Q.push(v) ;
}
}
}
}
return 1 ;
}
int minans[maxn] , maxans[maxn] ;
int la[maxn] , lb[maxn] , lc[maxn] ;
int da[maxn] , db[maxn] , dc[maxn] ;
int main(){
int i ,t , n , ml , md , a , b , c , T = 1 ;
cin>>t ;
while(t--){
cin>>n>>ml>>md ;
memset(g , -1 , (n+2)*sizeof(int)) ;
id = 0 ;
for(i = 1 ; i <= ml ; i++){
scanf("%d%d%d" ,&a , &b , &c) ;
add(a , b , c) ;
la[i] = a , lb[i] = b , lc[i] = c ;
}
for(i = 1 ; i <= md ; i++){
scanf("%d%d%d" ,&a , &b , &c) ;
add(b , a , -c) ;
da[i] = a , db[i] = b , dc[i] = c ;
}
for(i = 2 ; i <= n ; i++) add(i , i-1 , -1) ;
add(0 , 1 , 0) ;
add(1 , 0 , 0) ;
add(0 , n , 100000000) ;
printf("Case #%d: " , T++) ;
if(spfa(0 , n+1)){
for(i = 1 ; i <= n ; i++) maxans[i] = dist[i] ;
}
else{
puts("Not Exist!") ;
continue ;
}
memset(g , -1 , (n+2)*sizeof(int)) ;
id = 0 ;
for(i = 1 ; i <= ml ; i++) add(lb[i] , la[i] , -lc[i]) ;
for(i = 1 ; i <= md ; i++) add(da[i] , db[i] , dc[i]) ;
for(i = 2 ; i <= n ; i++) add(i-1 , i , 1) ;
add(0 , 1 , 0) ;
add(1 , 0 , 0) ;
add(n , 0 , -100000000) ;
if(spfa2(0 , n+1)){
for(i = 1 ; i <= n ; i++) minans[i] = dist[i] ;
}
else{
puts("Not Exist!") ;
continue ;
}
puts("Exist!") ;
for(i = 1 ; i <= n ; i++) printf("%d %d\n" , minans[i] , maxans[i]) ;
}
return 0 ;
}