1. 程式人生 > >【NOIP模擬賽】收銀員(一道好的查分約束題)

【NOIP模擬賽】收銀員(一道好的查分約束題)

bsp max int printf out 以及 void def 判斷

技術分享

技術分享

技術分享

/*
    s[]表示最優方案的序列中的前綴和,那麽s[23]就是最優方案
    由題意我們可以列出這樣一些式子:
    s[i]+s[23]-s[16+i]>=a[i] (i-8<0)
    s[i]-s[i-8]>=a[i] (i-8>0)//這兩個柿子選一個 
    b[i]>=s[i]-s[i-1]>=0
    然後可以化簡為:
    s[i]-s[16+i]>=a[i]-s[23]
    s[i]-s[i-8]>=a[i]
    s[i]-s[i-1]>=0
    s[i-1]-s[i]>=-b[i]
    這就滿足了差分約束的形式了
    呢麽就讓減數向被減數連一條邊,權值就是不等式右邊的常數
    對於s[23]我們二分答案,因此就轉化為對於一個既定的s[23],以及按照一定規則建好的圖,判斷這個條件下是否存在負環
    如果存在說明答案太小,如果不存在則答案合法,繼續松弛 
*/ #include<iostream> #include<cstdio> #include<queue> #include<cstring> #define maxn 30 #define INF 10000000000 using namespace std; int T,num; int s=24,vis[maxn],inq[maxn],head[maxn],dis[maxn],a[maxn],b[maxn]; struct node{ int to,pre,v; }e[200]; void Insert(int from,int to,int
v){ e[++num].to=to; e[num].v=v; e[num].pre=head[from]; head[from]=num; } bool spfa(){//判負環 for(int i=0;i<s;i++)dis[i]=-INF,inq[i]=vis[i]=0; dis[s]=0,inq[s]=1,vis[s]=0; queue<int>q; q.push(s); while(!q.empty()){ int u=q.front();q.pop(); inq[u]
=0; if(vis[u]>25)return 0; for(int i=head[u];i;i=e[i].pre){ int v=e[i].to; if(dis[v]>=dis[u]+e[i].v)continue; dis[v]=dis[u]+e[i].v; vis[v]++; if(!inq[v])inq[v]=1,q.push(v); } } return 1; } bool check(int s0){ num=0;memset(head,0,sizeof(head)); for(int i=0;i<24;i++){ if(i>=8)Insert(i-8,i,a[i]); else Insert(i+16,i,a[i]-s0); if(i)Insert(i,i-1,-b[i]),Insert(i-1,i,0); else Insert(i,s,-b[i]),Insert(s,i,0); } Insert(23,s,-s0),Insert(s,23,s0); return spfa(); } int main(){ freopen("cashier.in","r",stdin);freopen("cashier.out","w",stdout); scanf("%d",&T); while(T--){ int l=0,r=0; for(int i=0;i<24;i++)scanf("%d",&a[i]),l=max(l,a[i]); for(int i=0;i<24;i++)scanf("%d",&b[i]),r+=b[i]; if(l>r||!check(r)){puts("-1");continue;} if(l==0){puts("0");continue;} int ans; while(l<=r){ int mid=(l+r)>>1; if(check(mid))ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); } }

【NOIP模擬賽】收銀員(一道好的查分約束題)