算法与数据结构实验题 12.2 水杯
★实验任务
有 n 个水杯如图所示放置
从上到下,编号由 1 开始一直到 n,容量 ai 也依次增大(ai+1 > ai),如果
i 号杯子存的水超过了它的容量,则会像 i+1 号水杯流,以此类推现在给你两个操作
操作一: 1 x y 给 x 号杯子加 y 容量的水操作二: 2 x 查询 x 杯子里有多少水。
★数据输入
输入第一行为一个正整数 n
接下来 n 个元素,表示第 i 个水杯的容量接着输入操作的个数 q
接下来 q 行操作。
60%的数据 1<=n<=100,1<=ai,y<=100.
100%的数据 1<=n<=100000,1<=ai,y<=1000000000.
★数据输出
对于每个操作二,输出对应的值。
输入示例 | 输出示例 |
2 | 4 |
5 10 | 5 |
6 | 8 |
1 1 4 |
|
2 1 |
|
1 2 5 |
|
1 1 4 |
|
2 1 |
|
2 2 |
|
乍一看,暴力遍历,能过九个点。
仔细一想,我们多遍历了许多已经装满水的水杯,把这些水杯拿走就行了(拿不拿走对结果没影响,已经满了的水杯,浇了水,也会往下流。不如直接把它拿走)
这是是个并查集,分为两个集合,浇满水的集合和未浇满的集合。我们统计未浇满的集合就行了,浇满的做个标记就行了。不停地按顺序把浇满的位置退出未浇满的集合。
#include <cstdio>
#include <set>
using namespace std;
int cup[100100]={0}; // 容量集合
int a[100100]; //记录每个水杯当前的水的体积
int main()
{
set <int> s;
int n,q,x,y,t;
scanf ("%d",&n);
for (int i=1;i<=n;i++)
{
scanf ("%d",&a[i]);
s.insert(i);
}
scanf ("%d",&q);
set <int> ::iterator it;
set <int> ::iterator temp;
while(q--)
{
scanf ("%d %d",&t,&x);
if (t==1)
{
it=s.lower_bound(x);//查找水杯
scanf ("%d",&y);
while(y>0&&it!=s.end())
{
cup[*it]+=y;
if (cup[*it]>a[*it])
{
y=cup[*it]-a[*it];
cup[*it]=a[*it];
temp=it;
temp++;
s.erase(it); //删除这个水杯
it=temp;
}
else
y=0;
}
}
else
{
printf ("%d\n",cup[x]);
}
}
return 0;
}
在来一个标准并查集
#include <cstdio>
#include <cstring>
using namespace std;
int a[1000100]; //容量a
int s[1000100]; //父亲数组 集合的根节点时当前节点往后第一个未浇满的节点
int cup[1000100]; //实际容量
void Make_Set(void) //初始化
{
memset(s,-1,sizeof(s));
}
int Find(int x) //查找
{
if (s[x]<=0)
return x;
else
return (s[x]=Find(s[x]));
}
void Union(int root1,int root2) //合并
{
root1=Find(s[root1]);
root2=Find(s[root2]);
if (root1==root2)
return ;
if (s[root1]<s[root2])
s[root2]=root1;
else
{
if (s[root1]==s[root2])
s[root2]--;
s[root1]=root2;
}
}
void Plant(int x,int y,int n) //浇水
{
int i=Find(x);
while (y>0&&i!=n+1) //浇到地上 || 水浇完了 ,停止循环
{
cup[i]+=y;
if (cup[i]>a[i])
{
y=cup[i]-a[i];
cup[i]=a[i];
Union(i,i+1);
i=Find(i+1);
}
else
y=0;
}
}
int main()
{
int x,y,n,q,t;
scanf ("%d",&n);
for (int i=1;i<=n;i++)
scanf ("%d",&a[i]);
scanf ("%d",&q);
while (q--)
{
scanf ("%d %d",&t,&x);
if (t==1)
{
scanf ("%d",&y);
Plant(x,y,n);
}
else
{
printf ("%d\n",cup[x]);
}
}
return 0;
}
简化后的并查集
#include <cstdio>
using namespace std;
int p[100010];
int Find(int x)
{
if (p[x]<0)
return x;
else
return p[x]=Find(p[x]);
}
int main()
{
long long a[100010];
long long cup[100010]={0};
int n,q,t,x;
long long y;
scanf ("%d",&n);
for (int i=1;i<=n;i++)
{
scanf ("%lld",&a[i]);
p[i]=-1;
}
p[n+1]=-1;
a[n+1]=1<<29;
scanf ("%d",&q);
while (q--)
{
scanf ("%d%d",&t,&x);
if (t==1)
{
scanf ("%lld",&y);
while (cup[x]+y>a[x])
{
y=cup[x]+y-a[x];
cup[x]=a[x];
p[x]=Find(x+1);
x=p[x];
if (x==n+1)
break;
}
if (x!=n+1)
cup[x]+=y;
}
else
{
printf ("%lld\n",cup[x]);
}
}
return 0;
}