有n個節點,1<=n<=1e5,成環,每個節點有一些物品,相鄰節點可以傳遞一次物品,1和n相鄰。
瞬間就想起了
這類問題一般都可以轉化爲環的切割,如何求出切割呢?
可以求一個前綴和,如果sum[j]==sum[i](i<j)那麼[i+1,j]就可以切割成一段,首尾剩下的也可以併成一段。
我們會找到很多個sum的值,每一種值對應一個切割方法,值所對應的的多個節點就都是切割點。
在本題我們就通過前綴和找到所有切割,然後對於每一段,可以直接貪心模擬看看是否ok。
看了下官方題解,是枚舉第一個人的操作,然後遞推出其他人的操作。
因爲環狀的問題要同時兼顧兩邊,所以沒有什麼簡單的算法可以得到答案。
但是如果能在環上切一刀,那麼問題就變成了一個線段上的問題,這個時候問題會大大化簡,我們甚至可以直接貪心遞推來得到答案。
即枚舉+遞推。
本題代碼
#include<stdio.h>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n;
int a[maxn<<1];
int sum[maxn];
ll SUM;
map<int,int>MAP;
int cnt;
vector<int>vec[maxn];
vector<int>ans;
void read()
{
MAP.clear();
cnt=0;
SUM=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",a+i);
SUM+=a[i];
}
}
int id(int x)
{
if(!MAP.count(x))
{
MAP[x]=++cnt;
vec[cnt].clear();
}
return MAP[x];
}
bool OK(int l,int r)
{
if(r-l<=1) return true;
int jin=0;
for(int k=l+1;k<=r;k++)
{
int x = a[k]+jin;
if(x>=2||x<=-2) return false;
if(x==1)
{
ans.push_back(k);
ans.push_back(k+1);
}
else if(x==-1)
{
ans.push_back(k+1);
ans.push_back(k);
}
jin=x;
}
return !jin;
}
bool ok(int x)
{
ans.clear();
for(int i=0;i<(int)vec[x].size()-1;i++)
if(!OK(vec[x][i],vec[x][i+1]))
return false;
if(!OK(vec[x][(int)vec[x].size()-1],vec[x][0]+n))
return false;
return true;
}
void print()
{
puts("YES");
printf("%d\n",ans.size()/2);
for(int i=0;i<(int)ans.size();i+=2)
printf("%d %d\n",ans[i]%n+1,ans[i+1]%n+1);
}
int solve()
{
read();
if(SUM%n) return 0*puts("NO");
SUM/=n;
for(int i=0;i<n;i++)
{
a[i]-=SUM;
if(a[i]>2||a[i]<-2) return 0*puts("NO");
a[i+n]=a[i];
sum[i]=a[i];
if(i) sum[i]+=sum[i-1];
}
for(int i=0;i<n;i++)
vec[id(sum[i])].push_back(i);
for(int i=1;i<=cnt;i++)
if(ok(i))
{
print();
return 0;
}
puts("NO");
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
Money Transfers代碼
#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n;
ll sum[maxn];
map<ll,int>cnt;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%lld",sum+i);
for(int i=1;i<n;i++) sum[i]+=sum[i-1];
for(int i=0;i<n;i++) cnt[sum[i]]++;
int MAX=0;
for(map<ll,int>::iterator it = cnt.begin();it!=cnt.end();++it) MAX=max(MAX,it->second);
printf("%d\n",n-MAX);
return 0;
}