差分约束&&01规划

差分约束

给定不等式组,求可行解

最大值\(\to\) 最短路;最小值\(\to\)最长路

\(x \geq y+s\) 从 y 向 x 引一条边权为s的边

\(x \leq y+s\) 从 y 向 x 引一条边权为s的边

找一个源点,使其可以遍历所有的边

从源点求一遍最长/短路

结果1:如果存在负环,则原不等式组一定无解

结果2:如果没有负环,则\(dis[i]\)就是原不等式组的一个可行解

1.糖果 (求最小值)

  • 如果 X=1,表示第 A个小朋友分到的糖果必须和第 B个小朋友分到的糖果一样多;
  • 如果 X=2,表示第 A个小朋友分到的糖果必须少于第 B个小朋友分到的糖果;
  • 如果 X=3,表示第 A个小朋友分到的糖果必须不少于第 B个小朋友分到的糖果;
  • 如果 X=4,表示第 A个小朋友分到的糖果必须多于第 B个小朋友分到的糖果;
  • 如果 X=5,表示揶 A个小朋友分到的糖果必须不多于第 B个小朋友分到的糖果。

x=1 : \(A=B\) \(addd(x,y,0),addd(y,x,0);\)
x=2 : \(A \textless B \to B\geq A+1\) \(addd(x,y,1);\)
x=3 : \(A \le B\to B \geq A\) \(addd(y,x,0);\)
x=4 : \(A \textgreater B \to A \geq B+1\) \(addd(y,x,1);\)
x=5 : \(A\geq B\) \(addd(x,y,0);\)

2.区间(AcWing362)

给定 n 个区间 \([ai,bi]\)\(n\) 个整数 \(c_i\) 。你需要构造一个整数集合 Z,使得∀i∈[1,n],Z 中满足\(a_i≤x≤b_i\)的整数 x 不少于$c_i $个。

\(s[i]表示前i个数中选择了几个\)

n个条件转化为 \(s[y]-s[x-1]\geq c_i \to s[y]\geq s[x-1]+c_i\) \(addd(x,y,c_i)\)

\(s[i] \geq s[i-1],s[i]-s[i-1]\leq 1\) \(addd(i-1,i,0),addd(i,i-1,-1)\)

\(spfa(min a)\) 求最小值,最长路

3.出纳员问题(AcWing393 )

枚举答案\([0,n]\)

\(addd(0,24,ans),addd(24,0,-ans);\) 点24为最终的答案;

\(0 \to 24 的最长路若>ans,说明ans个人达不到要求,会形成正环,没有正环,说明答案可行\)

而且24小时是个循环,考虑边的指向

#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=30,M=3010;
int hd[N],to[M],nx[M],d[M],id;
int dis[N],a[N],t[N],c[N];
void addd(int x,int y,int z){to[++id]=y,d[id]=z,nx[id]=hd[x],hd[x]=id;}
queue<int>q;
bitset<N>ck;
bool spfa()
{
    int x=0;
    memset(dis,-0x3f,sizeof(dis));
    memset(c,0,sizeof(c));
    dis[x]=0;
    q.push(x);
    while(!q.empty())
    {
        x=q.front(),q.pop();
        ck[x]=0;
        for(int i=hd[x];i;i=nx[i])
        {
            if(dis[to[i]]<dis[x]+d[i])
            {
                dis[to[i]]=dis[x]+d[i];
                c[to[i]]=c[x]+1;
                if(c[to[i]]>=25) return 0;
                if(!ck[to[i]])
                {
                    ck[to[i]]=1;
                    q.push(to[i]);
                }
            }
        }
    }
    return 1;
}
void sol()
{
    int n,x;
    memset(t,0,sizeof(t));
    per(i,1,24) scanf("%d",a+i);
    cin>>n;
    per(i,1,n) scanf("%d",&x),t[x+1]++;
    per(ans,0,n)
    {
        id=0;
        memset(hd,0,sizeof(hd));
        addd(0,24,ans);
        addd(24,0,-ans);
        per(i,0,23) addd(i,i+1,0);//不能往下减人
        per(i,1,24) addd(i,i-1,-t[i]);//每个小时可用
        per(i,0,24)//每个小时需要
        {
            if(i<=16) addd(i,i+8,a[i+8]);
            else addd(i,i-16,a[i-16]-ans);
        }
        if(spfa())//没有环,符合要求
        {
            printf("%d\n",ans);
            return;
        }
    }
    puts("No Solution");
}
signed main()
{
    int tt;
	cin>>tt;
    while(tt--) sol();
	return 0;
}

01规划

1.观光奶牛(AcWing361)

求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。

设最大比值为X

$\frac{\sum f[i]}{\sum t[i]}>X \to \sum f[i]>\sum t[i]*x $

\(\sum f[i]-\sum t[i]*x>0\) ,所以变成找正环

2.最优高铁环

最小平均值环
\(X=(w_1+w_2+...+w_k)/k\)
\(w_1+...+w_k=k*X\)
\((w_1-x)+(w_2-x)+...+(w_k-x)=0\)
X 稍微大一点 =就会变成 < 就会出现负环
二分逼近找 X

#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=5e4+10,M=N<<1,inf=0x3f3f3f3f,p=13331;
const double eps=0.01;
int n,hd[N<<1],to[M],nx[M],d[M],id;
double dis[N];
void addd(int x,int y,int z){to[++id]=y,d[id]=z,nx[id]=hd[x],hd[x]=id;}
bitset<N>ck;
string s;
map<unsigned long long ,int>st;
int get(int x) {
    switch (x) {
    case 'S': return 1000;
    case 'G': return 500;
    case 'D': return 300;
    case 'T': return 200;
    default: return 150;
    }
}
bool dfs(int x,double mid)
{
    ck[x]=1;
    for(int i=hd[x];i;i=nx[i])
    {
        if(dis[to[i]]>dis[x]+mid+d[i])
        {
            dis[to[i]]=dis[x]+mid+d[i];
            if(ck[to[i]]||dfs(to[i],mid)) return 1;
        }
    }
    ck[x]=0;
    return 0;
}
bool sol(double x)
{
    per(i,1,n) dis[i]=inf;
    ck.reset();
    per(i,1,n) if(dfs(i,x)) return 1;
    return 0;
}
signed main()
{
    int m,co;
    unsigned long long u,v;
    cin>>m;
    per(i,1,m)
    {
        cin>>s;
        u=0,v=0,co=0;
        for(char &c:s)
        {
            if(c=='-') u=(u?u:v),v=0;
            else co+=(v?0:get(c)),v=v*p+c;
        }
        if(!st.count(u)) st[u]=++n;
        if(!st.count(v)) st[v]=++n;
        addd(st[u],st[v],-co);
    }
    double l=0,r=2147483648,mid,ans=-1;
    while(r-l>eps)
    {
        mid=(l+r)/2;
        if(sol(mid)) l=ans=mid;
        else r=mid;
    }
    cout<<round(ans)<<'\n';
	return 0;
}
posted @ 2023-02-13 17:51  f2021yjm  阅读(35)  评论(0编辑  收藏  举报