2019.12.13日常总结兼差分约束略解兼CSP-J2019第2题题解

差分约束

有这么一类问题,已知 A u A v l e n A_u-A_v \geq len ,求满足条件的最小的 A A

所谓差分约束,是解决此类问题的一种最好的方法。具体的做法就是用图论的方法来解决。在一张图上用边 ( u , v , l e n ) (u,v,len) 表示 A v A u l e n A_v-A_u\geq len ,然后就可以用SPFA等算法求出符合条件的 A A


为了更好的解决差分约束的问题,你应该知道以下算法:

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

【题意】: 幼儿园里有 N N 个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的 K K 个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
【输入格式】:
在这里插入图片描述
【输出格式】:
在这里插入图片描述
【样例】:
在这里插入图片描述
【数据范围】:
在这里插入图片描述
【思路】: 差分约束的模板题,记边 ( u , v , l e n ) (u,v,len) 表示 A v A u l e n A_v-A_u\geq len ,具体可看代码。至于求解,直接用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题

【题意】:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【思路】: 我们可以发现任何时刻,有效的票都最多只有 45 45 张。而且注意到题目中说的那句话:输入按开始乘车时间给出

在这里插入图片描述

既然如此,我们完全可以设计一个类似于单调队列的队列,如果队头的元素已经过时(那么它对于后面的记录也一定过时!),我们就残忍的 把它踢掉,此时,有用元素最多只有 45 45 条记录。至于寻找我们需要的记录,一个一个遍历也才 O ( 45 ) = O ( 1 ) O(45)=O(1)

时间复杂度:因为每个元素最多入队一次出队一次,所以时间复杂度最多为 T ( 45 × n ) = O ( n ) T(45 \times n)=O(n) ,完完全全可以 A C AC 本题。

【代码】:

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的前两题都挺简单(第一题就不讲了),第二题的话,认认真真思考一下,一定可以用很简单的方法做出来的!!!

发布了82 篇原创文章 · 获赞 4 · 访问量 1777

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/103533447