NKOJ 3485 数据
时间限制 : 30000 MS 空间限制 : 165536 KB
问题描述
Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:
试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这n 个待排序的数。
例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有3 个数(4,2,-1),第二组有4个数(1,2,3,4)。可是现在Mr_H 做的输入数据出了一些问题。
例如:2 1 9 3 2 按理说第一组数据有2 个数(1,9),第二组数据有3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!
现在 Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写个
程序,计算最少需要多少步才能将数据改得合法。
输入格式
第一行一个整数m,表示Mr_H 做的输入数据包含的整数个数。第二行包含m 个整数a[i],每个整数的绝对值不超过10000。
输出格式
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
样例输入
【样例输入1】
4
1 9 3 2
【样例输入2】
10
4 4 3 5 0 -4 -2 -1 3 5
样例输出
【样例输出1】
2
【样例输出2】
3
提示
【数据范围】
对于 20%的数据,m<=10, |a[i]|<=5;
对于60%的数据,m<=5000, |a[i]|<=10000
对于100%的数据,m<=100000, |a[i]|<=10000
来源 【2015多校联训4】 by YZ
法一,图论:
因为a[i]>=0时,i可以无损耗到达i+a[i]+1号节点,所以连一条权值为0单向边,注意终点可能超过n+1号,也是合法的。
因为一个节点到达k号后,往左或往右一格均视为操作一次,所以各个节点向左右连一条双向边,注意包括超过n+1的点。且第一个点与第二个点不连。
若a[1]<0 ,则至少需要 -a[i]的操作数,所以强行将a[1]改为0,并答案强制加-a[1]。
最后找出1到n+1的最短路
/*图论*/
#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
const int need=110003;
const int inf=1e9;
int n,nn;
//.....................................
inline void in_(int &d)
{
char t=getchar();bool mark=false;
while(t<'0'||t>'9') {if(t=='-') mark=true;t=getchar();}
for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
if(mark) d=-d;
}
//.....................................
struct bian
{
int la,en,len;
};
vector<bian> w;
#define pb(a,b,c) push_back((bian){a,b,c})
int tot,fi[need];
void add(const int &a,const int &b,const int &c)
{
tot++;
w.pb(fi[a],b,c);
fi[a]=tot;
}
//.....................................
int s,e;
struct fy
{
int dis,id;
bool operator< (const fy &b) const
{
return dis>b.dis;
}
};
priority_queue<fy> q;
#define pu(a,b) push((fy){a,b})
bool getans[need];
int dis[need];
void dijkstra(int s)
{
for(int i=1;i<=nn;i++) dis[i]=inf,getans[i]=false;
q.pu(0,s),dis[s]=0;
int x,y,t;
while(!q.empty())
{
while(!q.empty()&&getans[q.top().id]) q.pop();
x=q.top().id;
if(x==e) return ;
getans[x]=true;
q.pop();
for(t=fi[x];t;t=w[t].la)
{
y=w[t].en;
if(getans[y]) continue;
if(dis[y]>dis[x]+w[t].len)
{
dis[y]=dis[x]+w[t].len;
q.pu(dis[y],y);
}
}
}
}
//.....................................
int main()
{
w.resize(1);
int n;scanf("%d",&n);
int dans=0;
int a;
in_(a);
if(a<0) dans=-a,add(1,2,0);
else add(1,a+2,0);
nn=max(nn,a+2);
for(int i=2,a;i<=n;i++)
{
in_(a);
if(a>=0) add(i,i+a+1,0);
nn=max(nn,i+a+1);
add(i,i+1,1),add(i+1,i,1);
}
for(int i=n+1;i<=nn;i++) add(i,i+1,1),add(i+1,i,1);
s=1,e=n+1;
dijkstra(s);
cout<<dis[e]+dans;
}
法二-1:堆优化动规
#include<cstdio>
#include<set>
#include<iostream>
#include<queue>
using namespace std;
const int need=100003;
const int inf=1e9;
int a[need];
int f[need];
struct fy
{
int val,key;
friend bool operator< (const fy a,const fy &b)
{
//if(a.val==b.val) return a.key<b.key;
return a.val>b.val;
}
};
priority_queue<fy> q1;
#define pu(a,b) push((fy){a,b})
int q2=inf;
//............................................
inline void in_(int &d)
{
char t=getchar();bool mark=false;
while(t<'0'||t>'9') {if(t=='-') mark=true;t=getchar();}
for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
if(mark) d=-d;
}
//............................................
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++) in_(a[i]);
q1.pu(a[1]+1,a[1]+1);
for(int i=1;i<=n;i++)
{
//cout<<q1.top().key;
while(!q1.empty()&&q1.top().key<=i)
{
q2=min(q1.top().val-2*q1.top().key,q2);
q1.pop();
//cout<<q1.size();
}
f[i]=q2+i;
if(!q1.empty()) f[i]=min(f[i],q1.top().val-i);
q1.pu(f[i]+a[i+1]+i+1,a[i+1]+i+1);
}
cout<<f[n];
}