【九度】题目1554:区间问题

来源:互联网 发布:pta编程 编辑:程序博客网 时间:2024/05/20 18:43
题目描述:

给定一个数组,判断数组内是否存在一个连续区间,使其和恰好等于给定整数k。

输入:

输入包含多组测试用例,每组测试用例由一个整数n(1<=n<=10000)开头,代表数组的大小。
接下去一行为n个整数,描述这个数组,整数绝对值不大于100。
最后一行为一个整数k(大小在int范围内)。

输出:

对于每组测试用例,若存在这个连续区间,输出其开始和结束的位置,s,e(s <= e)。
若存在多个符合条件的输出,则输出s较小的那个,若仍然存在多个,输出e较小的那个。
若不存在,直接输出"No"。

样例输入:
5-1 2 3 -4 953-1 2 -372-1 10
样例输出:
2 3No

1 2

分析:本题的输入数据量过大,不宜采用暴力法。定义一个数组num[i],表示0~i号元素之和(第0号元素设为0).那么当num[j]-num[i]=k,j>i时,可知在i+1~j区间上的元素之和为k。现在问题转化为在一个数组num上找出一对最小的i,j使得,i<j,num[j]-num[i]==k。一种方法是将num的元素排序,然后依次遍历num的元素e,并且用二分查找法在有序的num上查找e+k。效率在nlogn内,但是比较麻烦,因为没一个num元素需要记录位置信息,排序后下标有所改变,而且每一次二分查找必须将所有值为e+k的元素都找出来,才能找出相对应最小的下标j。另一种简便的方法是采用map,first值为num值,second值为取first值的所有num的下标。这样可以在logn内查找到j,建立map需要nlogn时间,时间复杂度为nlogn,但代码量以及要考虑的情况简单多了。

代码如下:

#include<stdio.h>
#include<vector>
#include<map>
using namespace std;
#define N 1001
int num[N];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
num[0]=0;  //数组的第0个元素取为0
for(int i=1;i<=n;++i)
{
int tmp;
scanf("%d",&tmp);
num[i]=num[i-1]+tmp;   //num[i]存储数组从0~i的元素之和
}
int k;
scanf("%d",&k);
map<int,vector<int>> Hash;
map<int,vector<int>>::iterator it;
for(int i=1;i<=n;++i)
{
it=Hash.find(num[i]);
if(it==Hash.end())
{
Hash[num[i]].push_back(i);
}
else
{
(it->second).push_back(i);
}
}
int s=-1,e=-1;
bool flag=false;
for(int i=0;i<n;++i)
{
it=Hash.find(num[i]+k);
if(it!=Hash.end())
{
for(int j=0;j<(it->second).size();++j)
{
int t=(it->second)[j];
if(t>i)
{
s=i+1;
e=t;
flag=true;
break;
}
}
}
if(flag)
break;
}
if(flag)
printf("%d %d\n",s,e);
else
printf("No\n");
}
return 0;
}

0 0