差分约束
介绍:http://imlazy.ycool.com/post.1702305.html
性质:对于这种有一个未知数定死的差分约束系统,还有一个有趣的性质,
那就是通过最短路径算法求出来的一组解当中,所有未知数都达到最大值。
那么如果在一个未知数定死的情况下,要求其它所有未知数的最小值怎么办?只要反过来求最长路径就可以了。
1.POJ 1201 Intervals
构图:对于一个区间[a, b]和c, 则有 c<=s(b) – s(a),可构造一长度为c的有向边b -> a,
为确保联通,0<=s(i+1) – s(i) <=1; 即有i+1 ->i 权值为0,i->i+1权值为-1的两条边。
求最短路:求最大点到最小点的最短路。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
struct Node
{
Node(){}
Node(int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n, maxv, minv;
vector<Node>mp[50005];
void init()
{
int i;
maxv = -1;
minv = 60000;
for(i=0; i<=50000; ++i) mp[i].clear();
for(i=1; i<=n; ++i){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
++ b;
mp[b].push_back(Node(a, c));
if(b > maxv) maxv = b;
if(a < minv) minv = a;
}
for(i=minv; i<=maxv-1; ++i){
mp[i+1].push_back(Node(i, 0));
mp[i].push_back(Node(i+1, -1));
}
}
void spfa()
{
int i, j, u, v, w;
bool mark[50005];
int Min[50005];
queue<int> Q;
for(i=minv; i<=maxv; ++i){
mark[i] = 0;
Min[i] = -1;
}
Q.push(maxv);
mark[maxv] = 1;
Min[maxv] = 0;
while(!Q.empty()){
u = Q.front();
Q.pop();
mark[u] = 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v = mp[u][i].p;
w = mp[u][i].l;
if(Min[v] < Min[u] + w){
Min[v] = Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v] = 1;
}
}
}
}
printf("%d\n", Min[minv]);
}
int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)!=EOF){
init();
spfa();
}
return 0;
}
2.poj 1716 Integer Intervals
和poj1201差不多
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
struct Node
{
Node(){}
Node(int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n, maxv, minv;
vector<Node>mp[10005];
void init()
{
int i;
maxv = -1;
minv = 60000;
for(i=0; i<=10000; ++i) mp[i].clear();
for(i=1; i<=n; ++i){
int a, b;
scanf("%d %d", &a, &b);
++ b;
mp[b].push_back(Node(a, 2));
if(b > maxv) maxv = b;
if(a < minv) minv = a;
}
for(i=minv; i<=maxv-1; ++i){
mp[i+1].push_back(Node(i, 0));
mp[i].push_back(Node(i+1, -1));
}
}
void spfa()
{
int i, j, u, v, w;
bool mark[10005];
int Min[10005];
queue<int> Q;
for(i=minv; i<=maxv; ++i){
mark[i] = 0;
Min[i] = -1;
}
Q.push(maxv);
mark[maxv] = 1;
Min[maxv] = 0;
while(!Q.empty()){
u = Q.front();
Q.pop();
mark[u] = 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v = mp[u][i].p;
w = mp[u][i].l;
if(Min[v] < Min[u] + w){
Min[v] = Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v] = 1;
}
}
}
}
printf("%d\n", Min[minv]);
}
int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)!=EOF){
init();
spfa();
}
return 0;
}
3.poj 3169 Layout
假如b > a, s(b) – s(a) <=c,构造a -> b 有向边,权值为c。s(b) – s(a) >= c即s(a) – s(b) <=-c同理可以构造一有向边。
spfa最短路,无法到达返回-2,有负回路返回-1,其余返回最短路的值。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
struct Node
{
Node(){}
Node(int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n, A, B;
vector<Node>mp[1005];
const int inf = (1<<29);
void init()
{
int i;
scanf("%d %d", &A, &B);
for(i=1; i<=n; ++i) mp[i].clear();
for(i=1; i<=A; ++i){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
mp[a].push_back(Node(b, c));
}
for(i=1; i<=B; ++i){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
mp[b].push_back(Node(a, -c));
}
}
int spfa()
{
int i, u, v, w;
bool mark[1005];
int Min[1005], cnt[1005];
queue<int> Q;
for(i=1; i<=n; ++i){
mark[i] = 0;
Min[i] = inf;
cnt[i] = 0;
}
Q.push(1);
mark[1] = 1;
Min[1] = 0;
while(!Q.empty()){
u = Q.front();
Q.pop();
mark[u] = 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v = mp[u][i].p;
w = mp[u][i].l;
if(Min[v] > Min[u] + w){
Min[v] = Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v] = 1;
cnt[v]++;
if(cnt[v] > n) return -1;
}
}
}
}
if(Min[n] == inf) return (-2);
else return Min[n];
}
int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)!=EOF){
init();
printf("%d\n", spfa());
}
return 0;
}
4.poj 1364 King
a[si] + a[si + 1] + a[si + 2] +…+a[si+ni] < k ,即sum[si+ni] – sum[si-1] < k,
sum[si+ni] – sum[si-1] <= k – 1;从而构造出差分约束系统。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
struct Node
{
Node(){}
Node(int pp, int ll):p(pp), l(ll){}
int p, l;
};
int n;
vector<Node> mp[105];
void intograph()
{
int m, si, ni, ki;
char ch[10];
int i;
for(i=0; i<=n; ++i) mp[i].clear();
scanf("%d", &m);
while(m --){
scanf("%d %d %s %d", &si, &ni, ch, &ki);
if(ch[0] == 'g'){
int u, v;
u = si + ni;
v = si - 1;
mp[u].push_back(Node(v, -1-ki));
}else {
int u, v;
u = si - 1;
v = si + ni;
mp[u].push_back(Node(v, ki-1));
}
}
}
bool spfa()
{
int u, v, i, w;
int cnt[105], Min[105];
bool mark[105];
queue<int> Q;
for(i=0; i<=n; ++i){
cnt[i] = Min[i] = 0;
Q.push(i);
mark[i] = 1;
}
while(!Q.empty()){
u = Q.front();
Q.pop();
mark[u] = 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v = mp[u][i].p;
w = mp[u][i].l;
if(Min[v] > Min[u] + w){
Min[v] = Min[u] + w;
if(!mark[v]){
Q.push(v);
mark[v] = 1;
cnt[v] ++;
if(cnt[v] > n) return 0;
}
}
}
}
return 1;
}
int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d", &n)==1 && n){
intograph();
if(spfa()) puts("lamentable kingdom");
else puts("successful conspiracy");
}
return 0;
}
5.poj3159 Candies
这题分析起来很清晰,只是时间卡得太紧了,vector,queue超时,把vector换成指针,queue改成数组,还要加io外挂。。。纠结了半天。。。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct Node
{
int p, l;
Node *next;
}*mp[30005];
int n, m;
const int maxn = 30005;
void Read(int &x)
{
char ch;
x = 0;
ch = getchar();
while(!(ch >= '0' && ch <= '9')) ch = getchar();
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
}
void intograph()
{
int a, b, c;
int i;
for(i=0; i<=n; ++i) mp[i] = NULL;
while(m --){
Read(a); Read(b); Read(c);
Node *temp = new Node;
temp->p = b;
temp->l = c;
temp->next = mp[a];
mp[a] = temp;
}
}
int spfa()
{
int u, v, i, w;
int Min[maxn];
bool mark[maxn];
int stack[maxn], top = 0;
for(i=1; i<=n; ++i){
Min[i] = 1<<29;
mark[i] = 0;
}
stack[top++] = 1;
mark[1] = 1;
Min[1] = 0;
while(top){
u = stack[--top];
mark[u] = 0;
Node *pp;
for(pp=mp[u]; pp; pp=pp->next){
v = pp->p;
w = pp->l;
if(Min[v] > Min[u] + w){
Min[v] = Min[u] + w;
if(!mark[v]){
stack[top++] = v;
mark[v] = 1;
}
}
}
}
return Min[n];
}
int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d %d", &n, &m)==2){
intograph();
printf("%d\n", spfa());
}
return 0;
}
6.hdoj 3666 THE MATRIX PROBLEM
哈尔滨区域赛的原题,比赛时还不会差分约束。。。
L<=a[i][j]*n[i]/m[j]<=U
加个log变形为: log(L) <= log(a[i][j]) + log(n[i]) – log(m[j]) <= log(U);
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
struct Node
{
int p;
double l;
Node(){}
Node(int pp, double ll):p(pp), l(ll){}
};
const int maxn = 805;
int n, m;
double U, L;
vector<Node> mp[maxn];
void add_edge(int u, int v, double w)
{
mp[u].push_back(Node(v, w));
}
void intograph()
{
int i, j;
double a;
for(i=0; i<=n+m; ++i) mp[i].clear();
for(i=1; i<=n; ++i){
for(j=1; j<=m; ++j){
scanf("%lf", &a);
a = log(a);
add_edge(n+j, i, U-a);
add_edge(i, n+j, a-L);
}
}
}
int spfa()
{
int u, v, i;
double w, Min[maxn];
int cnt[maxn];
bool mark[maxn];
int stack[maxn], top = 0;
for(i=1; i<=n+m; ++i){
Min[i] = double(1<<29);
mark[i] = 0;
cnt[i] = 0;
}
stack[top++] = 1;
mark[1] = 1;
Min[1] = 0;
while(top){
u = stack[--top];
mark[u] = 0;
int sz = mp[u].size();
for(i=0; i<sz; ++i){
v = mp[u][i].p;
w = mp[u][i].l;
if(Min[v] > Min[u] + w){
Min[v] = Min[u] + w;
if(!mark[v]){
stack[top++] = v;
mark[v] = 1;
cnt[v] ++;
if(cnt[v] > (1.0*n+m)) return 0;
}
}
}
}
return 1;
}
int main()
{
// freopen("c:/aaa.txt", "r", stdin);
while(scanf("%d %d %lf %lf", &n, &m, &L, &U)==4){
L = log(L);
U = log(U);
intograph();
if(spfa()) puts("YES");
else puts("NO");
}
return 0;
}