BZOJ4795 环球巡演 【贪心】

传送门


SOL

把题看清楚,会发现从 i i i j j j点和返回与其他点对是相互独立的。处理出每一点对之间来回的次数,发现通过基本的贪心,只有一下几种情况:
一、全部单程。
二、优先 i i i j j j的双程,剩下单程。
三、优先 j j j i i i的双程,剩下单程。
四、优先 i i i j j j的双程,剩下优先优先 j j j i i i的双程,剩下单程。
五、优先 j j j i i i的双程,剩下优先优先 i i i j j j的双程,剩下单程。

以上五种策略分别在 四种票价大小顺序一定时最优,这是进一步贪心。
介于此题数据规模不大,代码实现上直接枚举比价就可以了。


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define cs const
#define ll long long
#define gc getchar()
#define in red()
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=(num<<1)+(num<<3)+(c^48);
	return num*f;
} 
cs int N=3e6+10;
cs ll inf=1e18;
typedef pair<int,int> pi;
#define m_p make_pair 
vector <int> G[N];
map <pi,int> f;
int cnt=1,n,d,m,num[2];
ll w[2][N],ans=0;
inline ll plan1(int pos){
	int up=G[pos].size();
	ll ret=0;
	for(int i=0;i<up;++i){
		ret+=w[0][pos+G[pos][i]];
	}
	return ret;
}
inline ll plan2(int pos){
	num[0]=0;
	int up=G[pos].size();
	ll ret=0;
	for(int i=0;i<up;++i){
		bool op=G[pos][i];			
		if(op==1&&num[0]){
			--num[0];
			ret+=w[1][pos];
		}
		else if(op==1)ret+=w[0][pos+1];
		else ++num[0]; 
	} 
	ret+=w[0][pos]*num[0]; 
	return ret;
}
inline ll plan3(int pos){
	num[1]=0;
	int up=G[pos].size();
	ll ret=0;
	for(int i=0;i<up;++i){
		bool op=G[pos][i];			
		if(op==0&&num[1]){
			--num[1];
			ret+=w[1][pos+1];
		}
		else if(op==0)ret+=w[0][pos];
		else ++num[1]; 
	} 
	ret+=w[0][pos+1]*num[1]; 
	return ret;
}
inline ll plan4(int pos,bool fer){
	num[1]=num[0]=0;
	int up=G[pos].size();
	ll ret=0;
	for(int i=0;i<up;++i){
		bool op=G[pos][i];
		if(op==(fer^1)&&num[fer]){
			--num[fer];
			ret+=w[1][pos+fer];
		}
		else if(op==(fer^1))++num[fer^1];
		else if(op==fer)++num[fer];
	}
	if(num[fer]>num[fer^1])ret+=num[fer^1]*w[1][pos+(fer^1)]+(num[fer]-num[fer^1])*w[0][pos+fer];
	else ret+=num[fer]*w[1][pos+(fer^1)]+(num[fer^1]-num[fer])*w[0][pos+(fer^1)];
	return ret;
}


signed main(){
	n=in,d=in;
	int u=-1,v;
	for(int i=1;i<=d;++i){
		v=in;
		if(u!=-1){
			if(u==v)continue;
			int now=f[m_p(u,v)];
			if(now){
				G[(now/2)*2].push_back(now-(now/2)*2);
			} 
			else{
				f[m_p(u,v)]=++cnt;
				f[m_p(v,u)]=++cnt;
				G[f[m_p(u,v)]].push_back(0);
			}
		}
		u=v;
	}
	for(int i=2;i<=cnt;++i)w[0][i]=w[1][i]=inf;
	m=in;
	for(int i=1;i<=m;++i){
		int u,v,p;
		char t;
		u=in;v=in;t=gc;p=in;
		int now=f[m_p(u,v)];
		if(!now)continue;
		w[0][now]=min(w[0][now],1ll*p);
		if(t=='R')w[1][now]=min(w[1][now],1ll*p);
	}
	for(int i=2;i<=cnt-1;i+=2){
		ll now=plan1(i);
		if(w[1][i]!=inf&&w[1][i]<w[0][i]+w[0][i+1])now=min(now,plan2(i));
		if(w[1][i+1]!=inf&&w[1][i+1]<w[0][i]+w[0][i+1])now=min(now,plan3(i));
		if(w[1][i]!=inf&&w[1][i+1]!=inf)now=min(min(plan4(i,0),plan4(i,1)),now); 
		ans+=now;
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值