题目:
思路:费用流吧,,欢快水掉,,不过貌似还有一种神建图?还有一种神dp?都不太会啊,,
很幸运的搞明白了两种神奇的做法,,,
费用流:看成三个块,,,可以看出每个块内至多有k个对吧,,
可以把一种合法解分解为不超过k条不相交路径,,路径端点满足相差至少为n,,,为什么这么想呢?不知道啊,,貌似是大神在搞k = 1时想出来的,,确实k = 1比较好想,,然而怎么证明?反证法好了,,对应啊,,网络流对应一定是合法解,,因为假设不合法,,那么某个n里有多于k个那一定是多于k条路而这不可能啊,,
合法解对应网络流,,,这里i个合法解向第i+k个连,,,对应网络流,,神啊!orz TA orz 韩宵月 orz faebdc
dp:一看啊k好小啊,,可以暴力枚举三个块内选的个数,,(准确的说前两个就可以了)
f d,i,j,k表示,在3个块内前d个,各自已经选了i,j,k个,,然后块内枚举保证了合法,,跨块之间可以判断一下,,,然后由合法的i,j,k转移过来即可啊,,边界非常多,,共8种情况,,,我也是醉了啊,,,我dp真是弱爆了!!!
faebdc 神dp代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 605;
const int M = 11;
int n,m;
int xu[N][3];
int dp[M][M][M];
int ans=-987654321;
int main()
{
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
int i,j,k,d;
int s,t;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&xu[i][0]);
for(i=1;i<=n;i++)
scanf("%d",&xu[i][1]);
for(i=1;i<=n;i++)
scanf("%d",&xu[i][2]);
for(s=0;s<=m;s++)
{
for(t=0;t<=m;t++)
{
memset(dp,0xc0,sizeof dp);
dp[0][0][0]=0;
for(d=1;d<=n;d++)
{
for(i=s;i>=0;i--)
{
for(j=t;j>=0;j--)
{
if(j+s-i>m)
continue;
for(k=m;k>=0;k--)
{
if(k+t-j>m)
continue;
int now=0;
if(i)
{
now+=xu[d][0];
if(j)
{
now+=xu[d][1];
if(k)
{
now+=xu[d][2];
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-1]+now);
now-=xu[d][2];
}
if(k+t-j<m)
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+now);
now-=xu[d][1];
}
if(j+s-i<m)
{
if(k)
{
now+=xu[d][2];
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+now);
now-=xu[d][2];
}
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]+now);
}
now-=xu[d][0];
}
if(j)
{
now+=xu[d][1];
if(k)
{
now+=xu[d][2];
dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k-1]+now);
now-=xu[d][2];
}
if(k+t-j<m)
dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k]+now);
now-=xu[d][1];
}
if(k)
{
now+=xu[d][2];
dp[i][j][k]=max(dp[i][j][k],dp[i][j][k-1]+now);
now-=xu[d][2];
}
}
}
}
}
for(i=0;i<=m;i++)
ans=max(ans,dp[s][t][i]);
}
}
printf("%d\n",ans);
return 0;
}