1. 程式人生 > >【差分約束系統】【最短路】【spfa】CDOJ1646 窮且益堅, 不墜青雲之誌。

【差分約束系統】【最短路】【spfa】CDOJ1646 窮且益堅, 不墜青雲之誌。

put pac 時間復雜度 edge 系列 string pri class emp

求一個有n個元素的數列,滿足任意連續p個數的和不小於s, 任意連續q個數的和不大於t。

令sum[i]表示前i項的和(0<=i<=n,sum[0]=0) 那麽題目的條件可轉化為: sum[i]-sum[i-p]>=s (p<=i<=n) sum[i]-sum[i-q]<=t (q<=i<=n) 將第一個不等式取反,得到 sum[i-p]-sum[i]<=-s(p<=i<=n)

於是問題轉化為求一系列不等式的解,這是一個典型的差分約束問題。 考慮最短路徑的性質,令dis[i]表示從s到i的最短路,則對於圖中存在的一條邊(u,v),有 dis[v]<=dis[u]+w(u,v),即dis[v]-dis[u]<=w(u,v); 類比不等式,於是可建圖,i向i-p引長度為-s的邊,i-q向i引長度為t的邊。 然後運行bellmanford,如果存在負環,則無解, 否則所得到的最短路的值就是sum[i]的一個解。 時間復雜度:O(VE) 具體原理及證明見《算法導論》P387

註意這裏只需要求出可行解,故而建立一個虛擬結點的方法是可行的。

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,p,q,K1,K2;
queue<int>Q;
bool inq[510];
int dis[510],sumv[510];
int v[510*3],__next[510*3],e,w[510*3],first[510],cnts[510];
void AddEdge(int U,int V,int W){
	v[++e]=V;
	w[e]=W;
	__next[e]=first[U];
	first[U]=e;
}
bool spfa(const int &s)
{
	memset(dis,0x7f,sizeof(dis));
    dis[s]=0; Q.push(s); inq[s]=1; ++cnts[s];
    while(!Q.empty())
      {
        int U=Q.front();
        for(int i=first[U];i;i=__next[i])
          if(dis[v[i]]>dis[U]+w[i])
            {
              dis[v[i]]=dis[U]+w[i];
              if(!inq[v[i]])
                {
                  Q.push(v[i]);
                  inq[v[i]]=1;
                  ++cnts[v[i]];
                  if(cnts[v[i]]>n+1)
                    return 0;
                }
            }
        Q.pop(); inq[U]=0;
      }
    return 1;
}
int main(){
	scanf("%d%d%d%d%d",&n,&p,&q,&K1,&K2);
	for(int i=0;i+p<=n;++i){
		AddEdge(p+i,i,-K1);
	}
	for(int i=0;i+q<=n;++i){
		AddEdge(i,q+i,K2);
	}
	for(int i=0;i<=n;++i){
		AddEdge(n+1,i,0);
	}
	if(!spfa(n+1)){
		puts("No");
		return 0;
	}
	puts("Yes");
	for(int i=1;i<=n;++i){
		sumv[i]=dis[i]-dis[0];
	}
	for(int i=1;i<n;++i){
		printf("%d ",sumv[i]-sumv[i-1]);
	}
	printf("%d\n",sumv[n]-sumv[n-1]);
	return 0;
}

【差分約束系統】【最短路】【spfa】CDOJ1646 窮且益堅, 不墜青雲之誌。