2018.07.11【2018提高组】模拟C组

99 篇文章 1 订阅
58 篇文章 0 订阅

前言:OTL


JZOJ 1293 气象牛

题目:

在N个数中选择K个,使K最小并总误差不超过E


分析

选择两点的误差可以预处理,动态规划。
状 态 转 移 方 程 : f [ i ] [ j ] = min ⁡ ( f [ k ] [ j − 1 ] + e r r o r [ k ] [ i ] ) , 状态转移方程:f[i][j]=\min(f[k][j-1]+error[k][i]), f[i][j]=min(f[k][j1]+error[k][i]),
f [ i ] [ j ] 表 示 i 点 选 了 , 选 择 j 个 的 最 小 误 差 f[i][j]表示i点选了,选择j个的最小误差 f[i][j]ij


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define go(i,a,b) for (int i=a;i<b;i++)
using namespace std;
int n,e,a[101],er[101][101],f[101][101],ans[101];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int abs(int a){return (a<0)?-a:a;}
int min(int a,int b){return (a<b)?a:b;}
int main(){
	n=in(); e=in(); memset(f,42,sizeof(f));
	fo(i,1,n) a[i]=in();
	fo(i,1,n){
		go(j,1,i) er[0][i]+=abs(a[j]-a[i])<<1;
		fo(j,i+1,n) {
		    er[i][n+1]+=abs(a[j]-a[i])<<1;
		    go(k,i+1,j) er[i][j]+=abs(2*a[k]-a[i]-a[j]);
		}
	}
	f[0][0]=0; memset(ans,42,sizeof(ans));
	fo(i,1,n) fo(j,1,i) go(k,0,i) f[i][j]=min(f[i][j],f[k][j-1]+er[k][i]);//动态规划
	fo(i,1,n) fo(j,i,n) ans[i]=min(ans[i],f[j][i]+er[j][n+1]);//统计最小值
	fo(i,1,n) if (ans[i]<=e) return !printf("%d %d",i,ans[i]);
}

JZOJ 1294 轻轨

题目

有N个站点,容量为C的列车起点在1号站点,终点在N号站点,有K组牛群,每组数量为 M i M_i Mi,行程起点和终点分别为 S i S_i Si E i ( 1 &lt; = S i &lt; E i &lt; = N ) E_i(1&lt;=S_i&lt;E_i&lt;=N) Ei1<=Si<Ei<=N,最多有多少头牛可以搭乘轻轨。


分析

贪心,用终点从小到大排列,有直接模拟的,但是我用的是线段树(zkw线段树),改了一个下午!
一开始让线段树叶子节点设为容量,不断减去每次行程可走的牛的数量,并累加入答案中。
但是zkw线段树没有懒标记,本身就是永久化的标记,所以这就是为什么要上传标记。
使标记=Tree[n]-Tree[n>>1],让每一个节点的值都减除它父亲的值。
这里写图片描述,这就是痛苦!!!


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
struct node{int x,y,w;}e[50001];
int m,n,c,Min[1<<16],M=1,ans;
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
bool cmp(node x,node y){if (x.y!=y.y) return x.y<y.y; else return x.x>y.x;}
int Ask(int h,int t)
{
	int A1=0,A2=0,ans;
	for(h+=M,t+=M;h^t^1&&h<t;h>>=1,t>>=1)//直接跳到叶子节点走到根
	{
		A1+=Min[h];A2+=Min[t];
		if(!(h&1))A1=min(A1,Min[h+1]);//开区间
		if(t&1)A2=min(A2,Min[t-1]);//the same
	}
	ans=min(A1+Min[h],A2+Min[t]);//统计左右的答案
	for(int i=h>>1;i;i>>=1)ans+=Min[i];//走到根
	return ans;
}
void GetChanged(int k)
{
	int temp=min(Min[k<<1],Min[(k<<1)+1]);
	Min[k]+=temp,Min[k<<1]-=temp;Min[(k<<1)+1]-=temp;
}
void Update(int h,int t,int v)
{
	for(h+=M-1,t+=M+1;h^t^1&&h<t;h>>=1,t>>=1)//跳到叶子节点
	{
		if(!(h&1))Min[h+1]+=v;//开区间
		if(t&1)Min[t-1]+=v;//the same
		GetChanged(h>>1);GetChanged(t>>1);//修改父节点的值
	}
	for(h>>=1;h;h>>=1)GetChanged(h);//在父节点不断跳到根并修改值
}
int main(){
	m=in(); n=in(); c=in(); for(;(M<<=1)<n+2;);
	for (int i=1;i<=m;i++) e[i].x=in(),e[i].y=in(),e[i].w=in();
	stable_sort(e+1,e+1+m,cmp); Update(1,n,c);//建树
	for (int i=1;i<=m;i++){
		int g=min(Ask(e[i].x,e[i].y-1),e[i].w);//最多能有多少头牛坐车
		if (g>0) ans+=g,Update(e[i].x,e[i].y-1,-g);//修改线段树
	}
	return !printf("%d",ans);
}

JZOJ 1295 设计

题目

n头牛,有些牛相互喜欢,希望两人的距离 ≤ 某 d i s \leq 某dis dis内,同样也有一些牛相互不喜欢,希望两人的距离 ≥ 某 d i s \geq 某dis dis,求1号牛和N号牛之间最大距离。


分析

对于 1 ≤ i &lt; j ≤ n 1\leq i&lt;j\leq n 1i<jn,当希望两人距离 ≤ 某 d i s \leq 某dis dis,可表示为 d i s [ i ] + 某 d i s ≤ d i s [ j ] dis[i]+某dis\leq dis[j] dis[i]+disdis[j]
当希望两人距离 ≥ 某 d i s \geq 某dis dis,可表示为 d i s [ i ] + 某 d i s ≥ d i s [ j ] , 即 d i s [ j ] − 某 d i s ≤ d i s [ i ] dis[i]+某dis\geq dis[j],即dis[j]-某dis\leq dis[i] dis[i]+disdis[j]dis[j]disdis[i]
所以可以使用spfa或bellman-ford,但是会有负环,所以spfa或bellman-ford在最坏情况要跑O(NM)
那怎么判断负环,对于spfa,每次松弛操作统计某点走过多少次,如果超过n次直接退出(不存在方案),当从第1个点spfa仍然算不出第n个点说明有无数种方案。


代码

#include <cstdio>
#include <cctype>
#include <queue>
#define big 1073741823
using namespace std;
struct node{int y,w,next;}e[20001]; queue<int>q; bool v[1001];
int n,m1,m2,dis[1001],x,y,w,k,ls[1001],cnt[1001];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
bool spfa(int s){
	q.push(s); v[s]=1; dis[s]=0;
	while (q.size()){
		int x=q.front(); q.pop();
		for (int i=ls[x];i;i=e[i].next)
		if (dis[e[i].y]>dis[x]+e[i].w){
			dis[e[i].y]=dis[x]+e[i].w;
			if (cnt[e[i].y]==n) return 1;
			cnt[e[i].y]=cnt[x]+1;//统计次数
			if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
		}
		v[x]=0;
	}
	return 0;
}
int main(){
    n=in(); m1=in(); m2=in();
    for (int i=1;i<=n;i++) dis[i]=big;
    while (m1--) {x=in();y=in();w=in();e[++k]=(node){y,w,ls[x]};ls[x]=k;}//加入边
    while (m2--) {x=in();y=in();w=in();e[++k]=(node){x,-w,ls[y]};ls[y]=k;}//加入边
    if (spfa(1)) return !puts("-1");//负环
    else if (dis[n]==big) return !puts("-2"); else return !printf("%d",dis[n]);
}

后续

第三题是这次最的一道题,然后还是错了,233
洛谷 2933 气象测量 洛谷 1607 庙会班车(the same)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值