分析
其实第一眼看到这个数据范围的时候已经知道这道题正解是状压DP,然而当时并不会打。所以就打了个全排列暴搜还打挂了导致JZ第一次0分
设 f [ i ] [ j ] f[i][j] f[i][j]表示状态为 i i i , i i i为0/1二进制数表示哪一位 没用过/用过 的状态, j j j表示现在最右的农田为第 j j j个农田。
枚举上一个状态 i i i,上一个状态最右的农田 j j j,当前状态最右的农田 k k k。
令
t
=
i
∣
(
1
<
<
(
k
−
1
)
)
t=i∣(1<<(k−1))
t=i∣(1<<(k−1))
则有
- 当 a [ j ] < a [ k ] 时 : f [ t ] [ k ] = f [ i ] [ j ] 当a[j]<a[k]时:f[t][k]=f[i][j] 当a[j]<a[k]时:f[t][k]=f[i][j]
- 当 a [ j ] > = a [ k ] 时 : f [ t ] [ k ] = f [ i ] [ j ] + ( a [ k ] − a [ j ] ) ∗ 2 当a[j]>=a[k]时:f[t][k]=f[i][j]+(a[k]−a[j])∗2 当a[j]>=a[k]时:f[t][k]=f[i][j]+(a[k]−a[j])∗2
最后统计方案数就记录有多少个与最优解一致。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
int n;
ll a[20],f[1<<16][20],p[1<<16][20],ans,cnt;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=0;i<n;i++)
{
f[1<<i][i+1]=2*a[i+1];
p[1<<i][i+1]=1;
}
for(int i=1;i<(1<<n);i++)
{
for(int j=1;j<=n;j++)
{
if((i>>(j-1))&1)
{
for(int k=1;k<=n;k++)
{
if((i>>(k-1))&1) continue;
int t=i|(1<<(k-1));
ll x;
if(a[j]>a[k]) x=f[i][j];
else x=f[i][j]+(a[k]-a[j])*2;
if(x>f[t][k])
{
f[t][k]=x;
p[t][k]=p[i][j];
}
else if(x==f[t][k])
{
p[t][k]+=p[i][j];
}
}
}
}
}
for(int i=1;i<=n;i++)
{
ans=max(ans,f[(1<<n)-1][i]);
}
for(int i=1;i<=n;i++)
{
if(f[(1<<n)-1][i]==ans)
{
cnt+=p[(1<<n)-1][i];
}
}
cout<<ans+2*n<<' '<<cnt;
return 0;
}