poj1275(差分约束)

本文通过具体案例深入探讨了差分约束系统的核心概念及其在解决实际问题中的应用。文章详细阐述了如何通过挖掘约束条件来建立数学模型,并利用图论中的最短路径算法求解最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接:传送门

差分约束难点在约束条件的挖掘,约束条件找不出或找不全就无法准确的做出这类型题目。

初次碰到这道题时想了很久也没能将约束条件挖掘出来,后来参考了网上各位巨头的博客才A了此题,现在将该题的思路写出来,使自己印象更深刻。

令:

S[i]:0~i时刻雇佣的出纳员。

r[i]:每个时刻需要的出纳员。

t[i]:每个时刻应征的出纳员。

S[0]始终为0,为辅助点。

mid为对给出应征的出纳员的总数的二分值

则有:

S[i-1] - S[i] <= 0;

S[i] - S[i-1] <= t[i];

S[i] - S[j] <= mid - r[j]; ( i > j )

S[i] - S[j] <= -r[j];         ( i < j )

S[0] - S[24] <= -mid;

若存在从点0到点24的最短路,则mid为雇佣出纳员数的一个可行解。

然后进行二分,求得出纳员数的最小解。


#include <iostream>
#include <stdio.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <queue>
using namespace std;

#define size 100000
struct Edge
{
    int node,len;
    Edge*next;
}m_edge[size];
Edge*head[25];
int Ecnt;
int S[25];  //0-i时刻雇佣的出纳员
int r[25];  //每个时刻需要的出纳员
int t[25];  //每个时刻应征的出纳
bool flag[25];
int num[25];

void initData()
{
    fill( S , S+25 , 99999999 );
    memset( m_edge , 0 , sizeof(m_edge) );
    fill( head , head+25 , (Edge*)0 );
    memset( flag , false , sizeof(flag) );
    memset( num , 0 , sizeof(num) );
}

void mkEdge( int a , int b , int L )
{
    m_edge[Ecnt].node = a;
    m_edge[Ecnt].len = L;
    m_edge[Ecnt].next = head[b];
    head[b] = m_edge+Ecnt++;
}

bool spfa()
{
    queue<int>point;
    point.push( 0 );
    S[0] = 0;
    flag[0] = true;
    ++num[0];

    while( !point.empty() ){

        int t = point.front();
        point.pop();
        flag[t] = false;

        for( Edge*p = head[t] ; p ; p = p->next ){
            int u = p->node;
            int L = p->len;
            if( S[u] > S[t] + L ){
                S[u] = S[t] + L;

                if( !flag[u] ){
                    point.push(u);
                    flag[u] = true;
                    ++num[u];

                    if( num[u] > 25 ) return false;
                }
            }
        }
    }
    return true;
}

int main()
{
    int T;
    scanf("%d",&T);
    while( T-- ){

        memset( r , 0 , sizeof(r) );
        memset( t , 0 , sizeof(t) );
        Ecnt = 0;

        for( int i = 1 ; i <= 24 ; ++i ){
            scanf("%d",&r[i]);
        }
        int N,p;
        scanf("%d",&N);
        for( int i = 0 ; i < N ; ++i ){
            scanf("%d",&p);
            ++t[p+1];
        }

        int rt = N,lt = 0;
        int mid;
        int ans = -1;

        while( lt<=rt ){

            initData();
            mid = (lt+rt)/2;

            for( int i = 1 ; i <= 24 ; ++i ){
                mkEdge( i-1 , i , 0 );
                mkEdge( i , i-1 , t[i] );
            }
            for( int i = 1 ; i <= 24 ; ++i ){
                int j = (i+8)%25;
                if( i > j ) mkEdge( i , j+1 , mid-r[j+1] );
                if( i < j ) mkEdge( i , j , -r[j] );
            }
            mkEdge( 0 , 24 , -mid );

            if( spfa() ){
                rt = mid-1;
                ans = mid;
            }
            else lt = mid+1;
        }
        if( ans != -1 ) printf("%d\n",ans);
        else printf("No Solution\n");
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值