数据(图论)

匿名 (未验证) 提交于 2019-12-03 00:34:01
Mr_H出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的: 


  试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这n个待排序的数。 





  现在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


【样例1】
 3
 
【数据范围】
 
对于20%的数据,m<=10,  |a[i]|<=5;
对于60%的数据,m<=5000, |a[i]|<=10000

对于100%的数据,m<=100000, |a[i]|<=10000


分析:

1.当a[i]>=0,i号节点可以不加修改地到达i+a[i]+1号节点,于是连一条从i出发的权值为0的有向边,注意i+a[i]+1可以大于m+1;这一步当a[i]<0时可以直接忽略(思考为什么)

2.任意一个节点向左或向右一步都相当于是一次操作,于是左右各连一条权值为1的无向边;这一步当a[i]<0时不能忽略(思考为什么)

3.a[1]不能小于0。所以当a[1]小于0时,答案直接加上-a[1]并把a[i]变为0,继续后面的操作(思考为什么变成0就可以了)

4.a[1]和a[2]之间一定不能连无向边(即因为操作而连的边),举几个例子就知道了

5.注意细节:因为可能有些点连到了大于m+1的点,m+1可能通过这些大于它的点到达,所以大于m+1的点也需要左右连一条权值为1的无向边

6.当边连好之后,求出1节点到m+1节点的最短路就可以了


#include<cstdio> #include<algorithm> #include<queue> using namespace std; const int maxm=100005; const int INF=2000000000; int m,last[maxm*2],np=0; struct edge{int to,pre,w;}E[maxm*5]; struct data { 	int id,d; 	friend bool operator < (data a,data b) {return a.d>b.d;} };  char c;int flag; inline void scan(int &x) { 	flag=1; 	while((c<'0'||c>'9')&&c!='-') c=getchar(); 	if(c=='-') flag=-1,c=getchar(); 	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0'; 	x*=flag;  }  inline void addedge(int u,int v,int w) { 	E[++np]=(edge){v,last[u],w}; 	last[u]=np; }  int dis[maxm*2],done[maxm*2]; inline int Dijkstra() { 	priority_queue<data>pq; 	for(int i=0;i<=maxm+20000;i++) dis[i]=INF; 	dis[1]=0; 	pq.push((data){1,0}); 	while(!pq.empty()) 	{ 		data t=pq.top(); 		pq.pop(); 		int i=t.id,d=t.d; 		if(done[i]) continue; 		done[i]=1; 		for(int p=last[i];p;p=E[p].pre) 		{ 			int j=E[p].to,w=E[p].w; 			if(dis[j]>dis[i]+w) 			{ 				dis[j]=dis[i]+w; 				pq.push((data){j,dis[j]}); 			} 		} 	} 	return dis[m+1]; }   int main() { //	freopen("in.txt","r",stdin); 	scan(m); 	int k=0,x; 	scan(x); 	if(x<0) addedge(1,2,0),k-=x;//判断a[1]的正负  	else addedge(1,x+2,0); 	for(int i=2;i<=m;i++) 	{ 		scan(x); 		if(x>=0) addedge(i,i+x+1,0);//不加修改地到达  		addedge(i+1,i,1);//左右修改一次  		addedge(i,i+1,1); 	} 	for(int i=m+1;i<=m+20000;i++) addedge(i,i+1,1),addedge(i+1,i,1);//大于m+1的点  	int ans=k+Dijkstra(); 	printf("%d",ans); 	return 0; }

转载请标明出处:数据(图论)
文章来源: 数据(图论)
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!