題意
n個學生坐一排,每個學生要么朝左要么朝右
每秒鍾可以將當前狀態的任意幾對面對面的相鄰學生往自己的反方向轉動
要求恰好在k秒鍾之后保證沒有任何一對相鄰學生面對面
問是否存在解決方案
解題思路
首先可以想到(猜到)的是,如果剛開始已經沒有任何一對相鄰學生面對面了
那就直接輸出-1
否則一定存在解決方案
且解決方案的秒數范圍為:
每秒鍾把所有相鄰且面對面的學生反向,可以得到最少的時間mink
每秒鍾只讓一對相鄰且面對面的學生反向,可以得到最多的時間maxk
只要mink<=k<=maxk
,則存在解決方案
當然,在取maxk的過程中不能隨意選一對學生改變(最好不要),而是按照取mink的過程的先后順序,把多對學生拆解成一對對再進行改變
所以先是模擬和預處理,每次搜索一遍當前狀態,如果檢測到 x 對相鄰學生面對面,則讓maxk+=x,mink+=1
直到x==0
(得到答案)或者mink>k
(要求步數太少,不存在方案)時,退出模擬
然后就開始處理答案(貪心,讓時間變得更多)
我們讓kk=mink
,以kk<k
作為循環條件
比如在取mink的過程中某一秒交換了5對學生
那么最多就可以把這5對分成5秒進行交換
所以只要在mink過程中的某一秒交換的學生沒有用完,就可以讓kk+=1,表示我們又浪費了一秒鍾(又拓展出了一秒鍾使得這個解決方案更接近要求的k)
如果在某次輸出后,這一過程中交換的學生對全部用完了,先kk+=1,又因為用完也就表示實際上少了一秒的過程,kk-=1,中和一下,什么也沒發生……所以這里特殊處理下
在最后滿足kk==k
時,就可以把剩下的沒有輸出完的按照mink的過程一秒為一組全部輸出
程序
用vector儲存取mink時每一秒鍾交換的位置
(405ms/2000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k;
ll maxk=0,mink=0,d;
char str[3050];
vector<int> v[3000050];
void solve()
{
int p=0,p2=0,kk=mink;
while(kk<k)//k與mink之間的差值就是要增加的秒數
{
cout<<"1 "<<v[p][p2]<<'\n';//前面每秒交換一對就行
p2++;
if(p2==v[p].size())//第p秒的所有學生對都已經交換完的話
{
p2=0;
p++;//換至下一秒
}
else
kk++;//否則讓kk+1
}
cout<<v[p].size()-p2;//特殊處理中間這一個,經過上面的每秒一人處理后,第p秒還剩下size-p2個學生沒有處理
for(int i=p2;i<v[p].size();i++)//接下來讓剩下的全部輸出
cout<<' '<<v[p][i];
cout<<'\n';
p++;//第p秒處理完了,切換至下一秒
while(p<mink)//只要p<mink,則接下來每一秒就按照取mink時的過程全部輸出
{
cout<<v[p].size();
for(int i=0;i<v[p].size();i++)
cout<<' '<<v[p][i];
cout<<'\n';
p++;
}
}
void check(int p)
{
for(int i=1;i<n;)
{
if(str[i]=='R'&&str[i+1]=='L')
{
str[i]='L';
str[i+1]='R';//更新
v[p].push_back(i);
i+=2;
}
else
i++;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>k>>(str+1);
check(0);//下標從0開始
while(v[mink].size()&&mink<=k)//如果還有面對面的學生,並且mink不大於給定的k時,繼續執行
{
maxk+=v[mink].size();//最多次數加上size
mink++;//最少次數加1
check(mink);
}
if(mink<=k&&k<=maxk)//給定的k在范圍內的話就處理輸出
solve();
else
cout<<"-1\n";
return 0;
}