问题描述
最近的m天盾神都去幼儿园陪小朋友们玩去了~
每个小朋友都拿到了一些积木,他们各自需要不同数量的积木来拼一些他们想要的东西。但是有的小朋友拿得多,有的小朋友拿得少,有些小朋友需要拿到其他小朋友的积木才能完成他的大作。如果某个小朋友完成了他的作品,那么他就会把自己的作品推倒,而无私地把他的所有积木都奉献出来;但是,如果他还没有完成自己的作品,他是不会把积木让出去的哟~
盾神看到这么和谐的小朋友们感到非常开心,于是想帮助他们所有人都完成他们各自的作品。盾神现在在想,这个理想有没有可能实现呢?于是把这个问题交给了他最信赖的你。
输入格式
第一行为一个数m。
接下来有m组数据。每一组的第一行为n,表示这天有n个小朋友。接下来的n行每行两个数,分别表示他现在拥有的积木数和他一共需要的积木数。
输出格式
输出m行,如果第i天能顺利完成所有作品,输出YES,否则输出NO。
样例输入
2
2
2 2
1 3
3
1 5
3 3
0 4
样例输出
YES
NO
数据规模和约定
1<=n<=10000,1<=m<=10。
第一次提交
#include <iostream>
#include<string>
using namespace std;
typedef struct
{
int now;//该小朋友目前手中所有的积木
int all;//该小朋友完成创作一共需要的积木数量
int c;//该小朋友完成创作还差多少积木
}tak;
//冒泡排序
void Sort(tak *p,int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i;j++)
{
if(p[j].c>p[j+1].c)
swap(p[j],p[j+1]);
}
}
int main()
{
tak p[10000];
int m;
cin>>m;
string s[m];//用来保存m天的输出结果
for(int t=0;t<m;t++)
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i].now>>p[i].all;
p[i].c=p[i].all - p[i].now;//构造还小朋友需要的积木数量
}
//按照p[i].c升序排序
//整体求解的算法思路为贪心算法,比较简单
Sort(p,n);//按照还需要的积木数量对小朋友进行升序排序
int i,ex=0;
for(i=1;i<=n;i++)//注意如果正常跳出循环i=n+1
{
if(ex>=p[i].c){
ex=ex+p[i].now;
}else{
s[t]="NO";
break;//贪心不下去了
}
}
if(i>n) s[t]="YES";//所有小朋友都完成的创作
else s[t]="NO";//所有小朋友有人未完成创作
}
for(int i=0;i<m-1;i++)
cout<<s[i]<<endl;//m-1行
cout<<s[m-1];//第m行
return 0;
}
笔者想了想,既然能够通过6组数据,那么算法肯定是没有问题的,问题肯定出现在时间复杂度上面。按照这一次提交的代码,需要对所有的小朋友按照还需要的积木数量进行升序排序,我写的冒泡排序O(n2),再一次遍历需要O(n),一共有m组数据,所以算法的时间复杂度为O(m*n2)
第二次提交
这一次我将冒泡排序改成了合并排序,合并排序的时间复杂度为O(nlogn),一共有m组数据,所以算法的时间复杂度为O(m*nlogn)
#include <iostream>
#include<string>
using namespace std;
typedef struct
{
int now;//该小朋友目前手中所有的积木
int all;//该小朋友完成创作一共需要的积木数量
int c;//该小朋友完成创作还差多少积木
}tak;
//把时间复杂度降低
//三个指针实现归并排序
void merge(tak a[],int l,int r,int mid)
{
tak temp[r-l+1];
int i,j,k;
i=l;//左半部分指针
j=mid+1;//右半部分指针
//k是临时数组指针
k=0;//临时数组初始化为0
while(i<=mid&&j<=r&&k<r-l+1)
{
if(a[i].c<a[j].c)
temp[k++]=a[i++];
else
temp[k++]=a[j++];
}
while(i<=mid)
temp[k++] = a[i++];
while(j<=r)
temp[k++] = a[j++];
//把临时数组再复制回去a数组
k=0;
for(int p=l;p<=r;p++)
a[p]=temp[k++];
}
//分治递归,合并排序
void merge_sort(tak a[],int l,int r)
{
if(l>=r) return ;
int mid=(l+r)/2;
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
merge(a,l,r,mid);//这一步实现了合并临时数组和复制临时数组
}
int main()
{
tak p[10000];
int m;
cin>>m;
string s[m];//用来保存m天的输出结果
for(int t=0;t<m;t++)
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i].now>>p[i].all;
p[i].c=p[i].all - p[i].now;//构造还小朋友需要的积木数量
}
//按照p[i].c升序排序
//整体求解的算法思路为贪心算法,比较简单
merge_sort(p,1,n);//按照还需要的积木数量对小朋友进行升序排序
int i,ex=0;
for(i=1;i<=n;i++)//注意如果正常跳出循环i=n+1
{
if(ex>=p[i].c){
ex=ex+p[i].now;
}else{
s[t]="NO";
break;//贪心不下去了
}
}
if(i>n) s[t]="YES";//所有小朋友都完成的创作
else s[t]="NO";//所有小朋友有人未完成创作
}
for(int i=0;i<m-1;i++)
cout<<s[i]<<endl;//m-1行
cout<<s[m-1];//第m行
return 0;
}
这一次,居然所有数据都通过了。通过这次实验,空间复杂度科研牺牲,但是时间复杂度必须尽可能降低。特别是贪心算法,时间复杂度的花费都集中在排序上面。