题解:P10192 [USACO24FEB] Moorbles S
一看到这道题就感觉是爆搜,于是我们就枚举每一次Bessie的可能选择,选可能的最小字典序的序列,写出了这样的代码:
#include <iostream>
using namespace std;
int T,n,m,k;
int a[300010][10];
bool check(int p,int sum) //判断在第p轮时Elsie有sum颗弹珠是否必胜
{
while(p<=m)
{
if(sum<=0)
{
return false; //输了
}
int jsmax=0; //最大的可能出的奇数
int osmax=0; //最大的可能出的偶数
int jsmin=1e9; //最小的可能出的奇数
int osmin=1e9; //最小的可能出的偶数
for(int i=1;i<=k;i++)
{
if(a[p][i]%2==1)
{
jsmax=max(jsmax,a[p][i]);
jsmin=min(jsmin,a[p][i]);
}
else
{
osmax=max(osmax,a[p][i]);
osmin=min(osmin,a[p][i]);
}
}
if(jsmin!=1e9 && osmin!=1e9) //偶数和奇数都可能出,最大化损失
{
sum-=min(jsmax,osmax);
}
else if(osmin!=1e9) //只可能出偶数,最小化利益
{
sum+=osmin;
}
else if(jsmin!=1e9) //同理
{
sum+=jsmin;
}
p++; //开始下一轮
}
return sum>0;
}
void dfs(int p,int sum)
{
if(p>m)
{
return;
}
int jsmax=0; //最大的可能出的奇数
int osmax=0; //最大的可能出的偶数
int jsmin=1e9; //最小的可能出的奇数
int osmin=1e9; //最小的可能出的偶数
for(int i=1;i<=k;i++)
{
if(a[p][i]%2==1)
{
jsmax=max(jsmax,a[p][i]);
jsmin=min(jsmin,a[p][i]);
}
else
{
osmax=max(osmax,a[p][i]);
osmin=min(osmin,a[p][i]);
}
}
if(jsmin==1e9) //只出偶数,肯定得猜偶数
{
if(p==m)
{
cout<<"Even";
}
else
{
cout<<"Even ";
}
dfs(p+1,sum+osmin);
}
else if(check(p+1,sum-jsmax)) //判断猜偶数是否还可以必胜
{
if(p==m)
{
cout<<"Even";
}
else
{
cout<<"Even ";
}
dfs(p+1,sum-jsmax);
}
else //只能猜奇数了
{
if(p==m)
{
cout<<"Odd";
}
else
{
cout<<"Odd ";
}
if(osmin==1e9)
{
dfs(p+1,sum+jsmin);
}
else
{
dfs(p+1,sum-osmax);
}
}
}
int main()
{
cin>>T;
while(T--)
{
cin>>n>>m>>k;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=k;j++)
{
cin>>a[i][j];
}
}
if(!check(1,n)) //无解
{
cout<<-1<<endl;
continue;
}
dfs(1,n);
cout<<endl;
}
return 0;
}
很明显,这样会超时,于是考虑优化。
可以发现,在check的时候,我们做了很多重复的计算,导致时间复杂度退化到了 $O(n^{2})$。所以,我们可以预处理出在第ppp轮时可能的最大损失,这样就可以将时间优化到 $O(n)$。
上代码:
#include <iostream>
using namespace std;
int T,n,m,k;
int a[300010][10];
int f[300010]; //f[i]表示第i轮可能的最大损失
void dfs(int p,int sum)
{
if(p>m)
{
return;
}
int jsmax=0;
int osmax=0;
int jsmin=1e9;
int osmin=1e9;
for(int i=1;i<=k;i++)
{
if(a[p][i]%2==1)
{
jsmax=max(jsmax,a[p][i]);
jsmin=min(jsmin,a[p][i]);
}
else
{
osmax=max(osmax,a[p][i]);
osmin=min(osmin,a[p][i]);
}
}
if(jsmin==1e9)
{
if(p==m)
{
cout<<"Even";
}
else
{
cout<<"Even ";
}
dfs(p+1,sum+osmin);
}
else if(sum-jsmax>=f[p+1]) //最大损失也能扛得住,猜偶数必胜
{
if(p==m)
{
cout<<"Even";
}
else
{
cout<<"Even ";
}
dfs(p+1,sum-jsmax);
}
else
{
if(p==m)
{
cout<<"Odd";
}
else
{
cout<<"Odd ";
}
if(osmin==1e9)
{
dfs(p+1,sum+jsmin);
}
else
{
dfs(p+1,sum-osmax);
}
}
}
int main()
{
cin>>T;
while(T--)
{
cin>>n>>m>>k;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=k;j++)
{
cin>>a[i][j];
}
}
f[m+1]=1; //判断需要,要是0就直接输了
//这里和之前的check有点像
for(int i=m;i>=1;i--)
{
int jsmax=0;
int osmax=0;
int jsmin=1e9;
int osmin=1e9;
for(int j=1;j<=k;j++)
{
if(a[i][j]%2==1)
{
jsmax=max(jsmax,a[i][j]);
jsmin=min(jsmin,a[i][j]);
}
else
{
osmax=max(osmax,a[i][j]);
osmin=min(osmin,a[i][j]);
}
}
if(jsmin!=1e9 && osmin!=1e9)
{
f[i]=f[i+1]+min(jsmax,osmax);
}
else if(jsmin==1e9)
{
f[i]=max(1,f[i+1]-osmin);
}
else if(osmin==1e9)
{
f[i]=max(1,f[i+1]-jsmin);
}
}
if(f[1]>n)
{
cout<<-1<<endl;
continue;
}
dfs(1,n);
cout<<endl;
}
return 0;
}
写完了才发现根本不用递归,循环就够了

浙公网安备 33010602011771号