前言:OTL
JZOJ 1293 气象牛
题目:
在N个数中选择K个,使K最小并总误差不超过E
分析
选择两点的误差可以预处理,动态规划。
代码
#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组牛群,每组数量为 ,行程起点和终点分别为 和 ,最多有多少头牛可以搭乘轻轨。
分析
贪心,用终点从小到大排列,有直接模拟的,但是我用的是线段树(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头牛,有些牛相互喜欢,希望两人的距离 内,同样也有一些牛相互不喜欢,希望两人的距离 ,求1号牛和N号牛之间最大距离。
分析
对于
,当希望两人距离
,可表示为
当希望两人距离
,可表示为
所以可以使用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)