System of difference constraints

Problem Description


Given Some inequality on some variable (Xi , Xj , …. ) in form
Xj – Xi <= W
We need to determine whether we can assign values to variables so that all the given inequality is satisfiable or not ? If satisfiable, then output A solution.

This System is called System of difference constraints. This is a special case of linear programming and yet very much useful.

Solution

Believe it or not, This problem has an elegant solution using Shortest Path algorithm.
To understand Please go through this :
1. lec18
2. Introduction To Algorithm (3rd Edition) : 24.4 Section

So I am writing the results (no proof). Proofs are discussed in details in the suggested readings.

Constraint Graph


– For each variable we create a vertex.
– For each in-equality, Xj – Xi <= W , We give an edge (Vi,Vj) with cost W
haha
– Create a source vertex (S) and give an edge (S,Vi) with cost = 0 ( Without using source vertex,we can solve)

Example:
Finding values for the unknowns x1,x2,x3,x4,x5, satisfying the following 8 difference constraints
example

Constraint Graph with Solutions:
bel

Unsatisfiable constraints


Theorem. If the constraint graph contains a negative-weight cycle, then the system of differences is unsatisfiable.
Proof is given in the pdf file.

Now we know if there is a solution or not.

Determining a Possible Solution :


– If there is no negative cycle in the constraint graph, Then There is a solution for the system. One of the solution is
for each variable (Xi):
Xi = Shortest Path distance of Vi from the Source vertex in Constraint Graph.
What about other solutions???
Let x = {x1,x2,…, xn } be a solution to a system of difference con- straints, and let d be any constant. Then x + d = { x1 + d, x2 + d, . . . , xn + d } is a solution as well.

Shortest Path can be calculated from Bellman-Ford algorithm.
Other Results from The constraint Graph:
# Bellman-Ford maximizes x1 + x2 + …. + xn subject to the constraints xj – xi ≤ wij and xi ≤ 0
# Bellman-Ford also minimizes maximum{xi} – minimum {xi}

Practise Problem:
1. Uva 11478

Solution:
For each node v in the given graph, Create a variable D_v such that we have operated Halum (v,D_v ) on the vertex v.
We then Binary Search on the answer.
Let Ans = X. We need to determine is X can possibly be our answer.
for each edge (u,v) , We get,
D_u - D_v + cost  of  the  edge >= X
=> D_v - D_u <= cost - X

This is just difference constraint discussed above. We can check whether we can satisfy all these conditions.

Lastly output the maximal answer.

Click to See Code

#include <bits/stdc++.h>
using namespace std;

struct Edge{
    int u,v,w;
};
vector<Edge>edge;
int n,e,u,v,w;
vector<pair<int,int > >G[505];

int d[505];
bool inq[505];
int Times[505];


bool spfa()
{
    queue<int>q;   // I have omitted Source vertex by pushing all the variables in queue with distance = 0
    for(int i = 1;i <= n; i ++ ) {
        d[i]= 0;
        q.push(i);
        inq[i] = 1;
        Times[i] = 1;
    }
    
    while(!q.empty()){
        int u = q.front(); q.pop();
        inq[u] = 0;
        for(int i = 0; i < G[u].size(); i ++ ) {
            int v = G[u][i].first, w = G[u][i].second;
            if(d[v] > d[u] + w) {
                d[v] = d[u]  + w;
                if(inq[v] == 0) {
                    inq[v] = 1;
                    q.push(v);
                    Times[v] ++;
                    
                    if(Times[v] > n+2 ) return 0;
                }
                
            }
        }
    }
    return 1;
}
bool can(int Mid)
{
    for(int i = 0; i <= n; i ++ ) {
        G[i].clear();
    }
    
    for(int i = 0; i < e; i ++ ) {
        u = edge[i].u, v = edge[i].v  , w = edge[i].w ;
        G[u].push_back(make_pair(v,w - Mid));
    }
    
    return spfa();
}
int main()
{
    while(scanf("%d %d",&n,&e)==2) {
        edge.clear();
        for(int i = 0; i < e; i ++ ) {
            scanf("%d %d %d",&u,&v,&w);
            edge.push_back({u,v,w});
        }
        if(can(10001)) {
            printf("Infinite\n");
            continue;
        }
        int low = 1, high = 10000, mid, ans = -1;
        while(low <= high ) {
            mid = (low+high) /2;
            if(can(mid)) {
                ans = mid;
                low  = mid+1;
            }
            else high= mid-1;
        }
        if(ans == -1 ) printf("No Solution\n");
        else printf("%d\n",ans);
        edge.clear();
    }
}


2. Uva 515 – King
3. POJ – 1201
4. POJ – 2983
5. POJ 1275
6. POJ – 3159
7. POJ – 3169

Dynamic Programming Problems

Problem 1 : CHEIFEFT
Idea:
From Codechef blog:

Let, dp[i][j] = expected maximal discount that we can obtain if we consider only first ‘i’ items and first ‘j’ discounts.

Consider the following two cases:

1) i-th item is chosen

We apply j-th discount to i-th item and get discount price[i] * discount[j] and turn to situation with (i-1) items and (j-1) discounts. So in this case, expected discount is equal to a[i] * b[j]+dp[i-1][j-1].

The probability of this happening is p = j/i. Since we must choose j items from i, and probability that i’th item will be chosen is equal to j/i.

2) i-th item is not chosen

The answer is dp[i-1][j].

The probability of this happening is 1-p.

I used to use recursive Version (Memoization). So here is my code

See details See https://discuss.codechef.com/questions/4100/chiefett-editorial

Click to See Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n,k;
int a[1005],p[1005];
int CS = 1;
int vis[N][N];
double dp[N][N];

double solve(int i,int j)
{
    if(j==0) return 0;
    if(i==0)return -1e8;
    if(vis[i][j] == CS) return dp[i][j];
    double res = j*1./i * (a[i] * p[j] * .01 + solve(i-1,j-1));
    if(i>=j) res += max((double)0,(1-j*1./i) * solve(i-1,j));
    vis[i][j] = CS;
    return dp[i][j] = res;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d %d",&n,&k);
        for(int i = 1; i <= n; i ++ ) scanf("%d",&a[i]);
        for(int i = 1; i <= k ; i ++ ) scanf("%d",&p[i]);
        sort(a+1,a+n+1);
        sort(p+1,p+k+1);
        printf("%.3lf\n",solve(n,k));
        CS ++;
    }
}

Problem 2 : LEMOVIE
Idea : DP + Combinatorics.
Solution : lemovie-editorial

Uva volume 10100-10199

Uva 10154 – Weights and Measures

Link: 10154 – Weights and Measures

Solution: DP
Discussion:

* Sort the turtle ascending according to their strength( Not residual strength). If equal strength, that according weight

Proof From Uva Forum:
Assume the solution would have a turtle that is stacked upon a turtle with smaller strength. Then the turtle with smaller strength has to carry also the weight of the turtle with bigger strength. So it is also possible to exchange them, because if the turtle with smaller strength can carry at least both weights, then of course the turtle with bigger strength can.

DP Solution from Uva :

Go through the list of turtles that is sorted ascending by strength (because I try to stack a tower of turtles upon ith turtle, therefore the turtle with smallest strength has to go first). In each step calculate the minimum weight that a stack of k turtles has. If it is impossible to have a stack of k turtles, define this as Infinity. Lets define this as mw(i,k) (i is number of step).

mw(i,k) = min(mw(i-1,k),mw(i-1,k-1)+weight(i)) if mw(i-1,k-1)+weight(i)<=strength(i)
else mw(i,k) = mw(i-1,k).

Answer is maximum k with mw(n,k)<infinity.

This problem helped me to think dp problems with a different angle.

Click to See Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5610;
pair<int,int> b[N];
int n=0;
int dp[N][N];
bool cmp(pair<int,int> a,pair<int,int> b)
{
    if(a.second == b.second) return a.first < b.first;
    return a.second < b.second;
}

int main()
{
   
    n=1;
    while(scanf("%d %d",&b[n].first,&b[n].second)==2) {
        n++;
    }
    sort(b+1,b+n,cmp);
    int f = 0;
    for(int i= 0;i < n; i ++) {
        for(int j= 0; j < n ; j++ ) dp[i][j] = 1e8;
    }
    dp[0][0] = 0;
    for(int i = 1; i < n; i ++ ) {
        dp[i][0] = 0;
        for(int j = 1; j < n ; j ++ ) {
            dp[i][j] = dp[i-1][j];
            if(dp[i-1][j-1] + b[i].first <= b[i].second) {
                dp[i][j] = min(dp[i][j] , dp[i-1][j-1] + b[i].first);
            }
        }
    }
    for(int i = 1; i < n; i ++ ) {
        for(int j = 1; j < n ; j ++ ) {
            if(dp[i][j] < 1e8) {
                f= max(f,j);
            }
        }
    }
    printf("%d\n",f);
    
}



Uva 10164 – Number Game

Link: 10164 – Number Game

Solution : Backtracking

The backtracking solution has complexity of O(n^3) but passes quickly. As it finds answer very quickly and return from recursion.

Three state:
calc(sum,cnt,ind) => sum = summation of numbers that are taken, cnt = # of elements taken, ind = current position.

Two options:
1.we can take current number then recurse
2. or not to take and recurse

If anytime we find a solution, we directly return.

Click to See Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1<<10;
int dp[N]; // This stores the number taken.
int a[N*2]; 
int n;
bool flag = 0;
void calc(int sum,int cnt, int ind)
{
    if(cnt==n){
        if(sum % n ==0){
            flag = 1; // found a solution
            printf("Yes\n");
            for(int i = 0; i < n;i ++) {
                if(i)printf(" ");
                printf("%d",dp[i]);
            }
            printf("\n");
        }
        return;
    }
    if(flag) return; // If already found a solution, then return
    if(ind >= 2*n-1) return;
    dp[cnt] = a[ind];
    calc(sum + a[ind], cnt+1, ind+1); // Taking a[ind]
    calc(sum,cnt,ind+1); // Not taking ind
    
}
int main()
{
    while(scanf("%d",&n) && n) {
        for(int i = 0; i < 2*n -1;i ++ ) {
            scanf("%d",&a[i]);
        }
        memset(dp,0,sizeof dp);
        flag = 0;
        calc(0,0,0);
        if(!flag) printf("No\n");
    }
    
}