差分约束
有这么一类问题,已知 ,求满足条件的最小的 。
所谓差分约束,是解决此类问题的一种最好的方法。具体的做法就是用图论的方法来解决。在一张图上用边
表示
,然后就可以用SPFA
等算法求出符合条件的
。
为了更好的解决差分约束的问题,你应该知道以下算法:
SPFA
算法求图中是否有正负环。分为递归版和非递归版。
【递归版代码】:
bool vis[N];int dis[N];
bool spfa(int u){
vis[u]=true;
for(to为u的所有后继节点){
if (可以用dis[u]更新dis[to]){
dis[to]=dis[u]+len(u,to);
if (vis[to]) return false;//false:有环
else if (!spfa(to)) return false;
}
}
return true;
}
【非递归版代码】:
int dis[N],cnt[N];bool vis[N];
bool spfa(int s){
queue<int> q;q.push(s);
memset(dis,128,sizeof(dis));
dis[s]=0;vis[s]=false;
while (q.size()){
int u=q.front();q.pop();vis[u]=true;
++cnt[u];if (cnt[u]==n) return false;
for(register int i=h[u];i;i=e[i].next){//以链式前向星为例
register int to=e[i].to;
if (dis[to]<dis[u]+e[i].len){//以求最长路为例
dis[to]=dis[u]+e[i].len;
if (vis[to]){
vis[to]=false;
q.push(to);
}
}
}
}
return true;
}
例题:洛谷P3275
【题意】: 幼儿园里有
个小朋友,lxhgww
老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww
需要满足小朋友们的
个要求。幼儿园的糖果总是有限的,lxhgww
想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
【输入格式】:
【输出格式】:
【样例】:
【数据范围】:
【思路】: 差分约束的模板题,记边
表示
,具体可看代码。至于求解,直接用SPFA
即可.
【代码】:
#define ll long long
const int N=100100;
struct node{
int next,to,len;
}e[N*3];int h[N],tot;
//(u,v,len) ==> a[v]-a[u]>=len ==> a[v]>=a[u]+len
inline void add(int a,int b,int c){
e[++tot]=(node){h[a],b,c};h[a]=tot;
}
int n,m,opt,x,y;
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
char c=0;ll x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
ll dis[N],cnt[N];bool vis[N];
bool spfa(int s){
queue<int> q;q.push(s);
memset(dis,128,sizeof(dis));
dis[s]=0;vis[s]=false;
while (q.size()){
int u=q.front();q.pop();vis[u]=true;
++cnt[u];if (cnt[u]==n) return false;
for(register int i=h[u];i;i=e[i].next){
register int to=e[i].to;
if (dis[to]<dis[u]+e[i].len){
dis[to]=dis[u]+e[i].len;
if (vis[to]){
vis[to]=false;
q.push(to);
}
}
}
}
return true;
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
opt=read();x=read();y=read();
if (opt==1) add(x,y,0),add(y,x,0);
else if (opt==2){
if (x==y){
printf("-1");
return 0;
}
add(x,y,1);
}
else if (opt==3) add(y,x,0);
else if (opt==4){
if (x==y){
printf("-1");
return 0;
}
add(y,x,1);
}
else add(x,y,0);
}
memset(cnt,0,sizeof(cnt));
memset(vis,1,sizeof(vis));
for(int i=n;i;i--)
add(0,i,1);
if (!spfa(0)) printf("-1");
else{
register ll ans=0;
for(int i=1;i<=n;i++)
ans+=dis[i];
cout<<ans;
}
return 0;
}
CSP-J 2019 第2题
【题意】:
【思路】: 我们可以发现任何时刻,有效的票都最多只有
张。而且注意到题目中说的那句话:输入按开始乘车时间给出。
既然如此,我们完全可以设计一个类似于单调队列的队列,如果队头的元素已经过时(那么它对于后面的记录也一定过时!),我们就残忍的 把它踢掉,此时,有用元素最多只有
条记录。至于寻找我们需要的记录,一个一个遍历也才
。
时间复杂度:因为每个元素最多入队一次出队一次,所以时间复杂度最多为 ,完完全全可以 本题。
【代码】:
const int N=100100;
struct node{
int price,times;
bool used;
}q[N];int head,tail,n;
int way[N],price[N],times[N];
inline void read_the_date(){
n=read();
for(int i=1;i<=n;i++){
way[i]=read();
price[i]=read();
times[i]=read();
}
}
long long answer;
inline void calc_answer(){
head=1;tail=0;
for(int i=1;i<=n;i++){
if (way[i]==0){
answer+=price[i];
q[++tail].price=price[i];
q[tail].times=times[i];
q[tail].used=true;
}
else{
while (head<=tail&&q[head].times<times[i]-45) head++;
register bool flag=true;
for(int j=head;j<=tail;j++)
if (q[j].used&&q[j].price>=price[i]){
q[j].used=false;
flag=false;break;
}
if (flag) answer+=price[i];
}
}
}
inline int HPXXZYY(){
read_the_date();
calc_answer();
cout<<answer;
return 0;
}
int main(){
return HPXXZYY();
}
【后记】: 每年CSP-J
的前两题都挺简单(第一题就不讲了),第二题的话,认认真真思考一下,一定可以用很简单的方法做出来的!!!