http://acm.hdu.edu.cn/showproblem.php?pid=4262
题意:给你一串珠子,开始手握第一号珠子。有三种操作:顺时针转动一个珠子,逆时针转一个珠子,取下一个珠子(此珠子必须在手上)。现给定取下珠子所要求的顺序,求操作的最少步数。同时还有一条件,取下珠子后顺时针方向的下一个珠子会滑落到被取珠子的位置,串长度减一。
题解:由于取珠子顺序被规定,即每个状态最后结果都一样,故可以采用贪心,每次采用最小最近的方式将要取下的珠子转到手上。如同链表删节点,分别用数组保存每个节点的前一个和后一个节点。而该串则用树状数组保存,目标珠子与当前手中珠子距离sum(target)-sum(now)即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
//const double eps=1e-7;
//const double INF=1e50;
//const double pi=acos(-1);
#define N 100005
#define M 10000
int tp[N],n,c[N],next[N],pre[N];
void add(int p,int d)//p位置上加d
{
while(p<=n)
{
c[p]+=d;
p+=p&(-p);//lowbit[i]=i&(-i);
}
}
int sum(int p)//a[1]+...+a[p]
{
int ret=0;
while (p)
{
ret+=c[p];
p-=p&(-p);
}
return ret;
}
void init()
{
int i;
for (i=1;i<=n;i++)
{
add(i,1);
next[i]=i+1;
pre[i]=i-1;
}
next[n]=1;
pre[1]=n;
}
int main()
{
freopen("a","r",stdin);
int i,j;
long long ans;
while (1)
{
scanf("%d",&n);
if (n==0) break;
for (i=1;i<=n;i++)
{
scanf("%d",&j);
tp[j]=i;
}
init();
tp[0]=1;
ans=0;
for (i=1;i<=n;i++)
{
int w1,w2;
w1=abs(sum(tp[i])-sum(tp[i-1]));//tp[i-1]不再表示上次取下的珠子的编号
w2=(n-i+1)-w1;
if (w1<w2) ans+=w1+1;
else ans+=w2+1;
next[pre[tp[i]]]=next[tp[i]];
pre[next[tp[i]]]=pre[tp[i]];
add(tp[i],-1);
tp[i]=next[tp[i]];//这里就用tp[i]保存第i个珠子被取走后手上拿的是几号珠子方便下次计算
}
cout<<ans<<endl;
}
return 0;
}