P5331题解

题目描述

P5331

Solution

给大家介绍一种暴力带一点小优化 AC 的方法。

首先看题发现这是到最小费用的模板题。

如果不了解建议先做模板

既然是模板那就少不了拆点,

我们将第 \(i\) 个哨站拆成入点和出点,

起点到当前哨站的出点连一条流量为 \(1\) ,残量为 \(0\)

当前点的出点到终点连一条流量为 \(1\) ,残量为 \(m\)

当前点的入点到终点连一条流量为 \(1\) ,残量为 \(0\)

一个出点到另一个入点连一条流量为 \(1\) ,残量为这两个哨站的频段差的绝对值。

接着就是用 spfa 跑最小费用。

提交之后,只有 \(80\)

看看时间限制 \(5s\) ,呵呵。

那只能在建边时加点优化。

接下来是本題的细节:建边时的一点小优化。

我们容易得出如果两个哨站之间的频段差的绝对值大于直接接到控制中心,

那么就不用建边。

最后还是上一下完整的代码。

代码

#include<bits/stdc++.h>
#define re register
#define end END
#define int long long
using namespace std;

const int INF = 1e9;
const int N = 2005;
int n,m,tot=1,dis[N],be,end,ans;
int head[N],pre[N],xb[N],flow[N],w[N];
//pre记录的是from xb是对应的边 flow是最小残量
struct Node
{
	int v,nxt,c;
	int cost;
}a[1010101];
bool f[N];

inline int max(int a,int b)
{
	if(a>b) return a;
	return b;
}

inline int min(int a,int b)
{
	if(a>b) return b;
	return a;
}//手写据说可以加速

inline void add(int u,int v,int c,int cost)
{
	a[++tot].v=v;
	a[tot].nxt=head[u];
	a[tot].c=c;
	a[tot].cost=cost;
	head[u]=tot;
}

inline int read()
{
	int x=0,f=1;char c=getchar();
	while(c>'9'||c<'0'){if(c == '-')f = -1;c = getchar();}
	while(c>= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}

inline int solve1(int x){return (x<<1)-1;}

inline int solve2(int x){return (x<<1);}//位运算似乎要快那么一点 

inline bool spfa()//spfa模板 
{
	for(re int i=1;i<=end;++i) dis[i]=INF,f[i]=0;
    //i++比++i慢那么一丢丢 
	queue<int> q;
	q.push(be);
	f[be]=1;
	pre[be]=0;
	pre[end]=-1;
	dis[be]=0;
	flow[be]=INF;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();f[u]=0;
		for(re int i=head[u];i!=0;i=a[i].nxt)
		{
			int v=a[i].v;
			int cost=a[i].cost;
			if(a[i].c>0&&dis[u]+cost<dis[v])
			{
				dis[v]=dis[u]+cost;
				pre[v]=u;
				xb[v]=i;
				flow[v]=min(flow[u],a[i].c);
				if(!f[v])
				{
					f[v]=1;
					q.push(v);
				}
			}
		}
	}
	if(pre[end]==-1) return false;
	else return true;
}

inline void add_(int u,int v,int c,int cost)
{
	add(u,v,c,cost);add(v,u,0,-cost);//网络瘤建边 
}

inline void build1()
{
	for(re int i=1;i<=n;++i)
	{
		int now=solve1(i);
		add_(be,now,1,0);//起点到当前点的出点连一条 
		add_(now,end,1,m);//当前点的出点到end连一条 
		add_(solve2(i),end,1,0);//当前点的入点到end连一条 
		for(re int j=1;j<i;++j)
			if(abs(w[i]-w[j])<m)//小优化 
			add_(now,solve2(j),1,abs(w[i]-w[j]));//出点到入点连一条 
	}
}

inline void put1(int x)
{
	if(x > 9) put1(x/10);
	putchar(x%10^48);
}
inline void put(int x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	put1(x);
	if(c >= 0) putchar(c);
}

signed main()
{
    scanf("%lld%lld",&n,&m);
    //n=read(),m=read();我也不知道为什么快读比scanf还慢 
    for(re int i=1;i<=n;++i) scanf("%lld",&w[i]);//;w[i]=read()
	be=solve1(n+1);end=solve2(n+1);//求解起点和终点 
	build1(); //建边 
	while(spfa())
	{
		int k=end;
		while(k!=be)
		{
			a[xb[k]].c-=flow[end];
			a[xb[k]^1].c+=flow[end];
			k=pre[k];
		}
		ans+=dis[end]*flow[end];
		//还是模板 
	}
	//put(ans);快输比printf还慢
	//不知道是不是写错了 
	printf("%lld",ans);
    return 0;
}

似乎现在被卡常了

posted @ 2022-05-08 09:26  starrylasky  阅读(27)  评论(0)    收藏  举报