差分约束

求最大值用最短路求解,求最小值用最长路求解,当然我们可以把图中所有的边权取负,求取最短路,两者是等价的。

求最小值
Intervals
题意:给定n个区间,[ai,bi]这个区间至少选选出ci个整数,求一个集合z,满足每个区间的要求,输出集合z的大小。
思路,用dis[i],表示从0到i最少需要多少个整数,则dis[b]-dis[a-1]>=c;dis[i]-dis[i-1]>=0;dis[i]-dis[i-1]<=1,dis[i]一定比dis[i-1]大,且最多大1,因为每个数只能选一次由于是从0开始,数组不能存负1,所以我们全部加一,用dis[i+1]表示0到i,其中,题目中还有隐含条件,0<=dis[i]-dis[i-1]<=1.约束条件找好了
统一一下方向,
dis[b+1]-dis[a]>=0;
dis[i]-dis[i-1]>=0;
dis[i-1]-dis[i]>=-1;
由于我的for是1到mx,所以这样写,你也可以让i+1和i比 那样for循环就从0到mx-1
代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<iostream>
#include<string>
#include<vector>
#define N 50000
#define inf 0x3f3f3f
using namespace std;
int net[N*4],fir[N*4],to[N*4],w[N*4];
int cnt,dis[N],vis[N];
void add(int x,int y,int z)
{
    net[cnt]=fir[x];
    fir[x]=cnt;
    to[cnt]=y;
    w[cnt]=z;
    cnt++;
}
void spfa(int n)
{
    memset(dis,0xc0,sizeof(dis));
    dis[0]=0;
    queue<int>q;
    q.push(0);
    vis[0]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=fir[u]; i; i=net[i])
        {
            int v=to[i];
            int ww=w[i];
            if(dis[v]<dis[u]+ww)
            {
                dis[v]=dis[u]+ww;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}
int main()
{
    cnt=1;
    memset(fir,0,sizeof(fir));
    int n,mx=0,u,v,w;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v+1,w);
        mx=max(mx,v+1);
    }
    for(int i=1; i<=mx; i++)
    {
        add(i-1,i,0);
        add(i,i-1,-1);
    }
    spfa(mx);//
    printf("%d\n",dis[mx]);
    return 0;
}

出纳员问题
题意 给几组测试样例,第一行是每小时最少需要的出纳员数量,然后给你申请者数量,表示在几时有一个人来申请
设r[i]为每小时最少需要的出纳员人数,t[i]为i时来应聘的人数

由于工作时间8小时确定,所以用sum数组表示t[i]的前缀和,
sum[i]-sum[i-8]>=第i个时刻需要的人数(r[i])
0<=sum[i]-sum[i-1]<=t[i]
当i<8的时候,i-8变成i-8+24→i+16
即sum[i]-sum[i+16]+x>=第i个时刻需要的人数(r[i])
sum[i]-sum[i-1]>=0;(差分隐含条件)
同时sum[i]-sum[i-1]<=在第i时刻应聘的人数(t[i])
sum[i]-sum[i+16]+sum[24]>=r[i];
统一一下不等式方向 跑最长路
sum[i]-sum[i-1]>=0;
sum[i-1]-sum[i]>=-t[i]
sum[i]-sum[i-8]>=r[i];
sum[i]-sum[i+16]+x>=r[i];
sum[24]-sum[0]>=x;(0时刻是不需要人的,所以x与sum[24]相等即可作为答案)
x即所求一天的最少出纳员数量,sum[24]-sum[0]
从小到大枚举x,用spfa判断是否可行

#include<stdio.h>
#include<string>
#include<iostream>
#include<string.h>
#include<queue>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
const int M=30;
const int N=1000;
int fir[M],to[N],w[N],net[N],cnt;
int dis[M],vis[M],co[M],t[M],r[M];
void init()
{
    memset(fir,0,sizeof(fir));
    cnt=0;
    memset(t,0,sizeof(t));
}

void add(int x,int y,int z)
{
    net[++cnt]=fir[x];
    fir[x]=cnt;
    to[cnt]=y;
    w[cnt]=z;
}
int spfa(int x)
{
    queue<int>q;
    mem(dis,-0x3f);
    mem(co,0);
    mem(vis,0);
    int st=0;
    while(!q.empty())
        q.pop();
    dis[st]=0;
    vis[st]=1;
    co[st]=1;
    q.push(st);
    while(!q.empty())
    {
        int u=q.front(),i;
        q.pop();
        vis[u]=0;
        for(i=fir[u];i;i=net[i])
        {
            int v=to[i];
            if(dis[v]<dis[u]+w[i])
            {
                dis[v]=dis[u]+w[i];
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                    co[v]++;
                    if(co[v]>24)
                        return 0;
                }
            }
        }
    }
    return dis[24]==x;
}
int solve(int x)
{
    memset(fir,0,sizeof(fir));
    cnt=0;
    for(int i=1;i<=24;i++)
    {
        add(i-1,i,0);
        add(i,i-1,-t[i]);
        if(i>8)
            add(i-8,i,r[i]);
        else
            add(i+16,i,r[i]-x);
    }
    add(0,24,x);
    return spfa(x);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        int x,flag=0,n;
        for(int i=1;i<=24;i++)
            scanf("%d",&r[i]);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            t[x+1]++;
        }
       // printf("%%%\n");
        for(int i=0;i<=n;i++)
        {
            if(solve(i)){
                printf("%d\n",i);
                flag=1;
                break;
            }
        }
        if(!flag)
        printf("No Solution\n");
    }
    return 0;
}

将所有权值取负跑最短路的代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int M=30;
const int N=1005;
int T,n,r[M],t[M],fir[M],to[N],nxt[N],w[N],num=0,dis[M],co[M],inf;
bool vis[M];
queue<int> q;
void init()
{
    memset(fir,0,sizeof(fir));
    memset(t,0,sizeof(t));
    num=0;
}
void build(int u,int v,int ww)
{
    num++;
    to[num]=v;
    nxt[num]=fir[u];
    w[num]=ww;
    fir[u]=num;
}
bool spfa(int x)
{
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(co,0,sizeof(co));
    int st=24;
    while(!q.empty()) q.pop();
    q.push(st);
    dis[st]=0;
    vis[st]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=fir[u]; i; i=nxt[i])
        {
            int v=to[i];
            if(dis[v]>dis[u]+w[i])
            {
                dis[v]=dis[u]+w[i];
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                    co[v]++;
                    if(co[v]>24) return 0;
                }
            }
        }
    }
    return 1;
}
bool solve(int x)
{
    memset(fir,0,sizeof(fir));
    num=0;
    for(int i=1; i<=24; i++)
    {
        build(i,i-1,0);
        build(i-1,i,t[i]);
        if(i>8) build(i,i-8,-r[i]);
        else build(i,i+16,x-r[i]);
    }
    build(24,0,-x);
    return spfa(x);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        for(int i=1; i<=24; i++) scanf("%d",&r[i]);
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            int x;
            scanf("%d",&x);
            t[x+1]++;
        }
        int flag=0;
        for(int i=0;i<=n;i++)
        {
            if(solve(i))
            {
                printf("%d\n",i);
                flag=1;
                break;
            }
        }

        if(!flag) printf("No Solution\n");
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值