D
可达性dp
只能往下往右,有障碍物,问能到的最远点。由于路径单调,不用搜索,直接可行性dp
char a[101][101];
int vis[101][101];
void solve(void){
int n,m;
cin>>n>>m;
rep(i,1,n){
rep(j,1,m){
cin>>a[i][j];
}
}
vis[1][1]=1;
int ans=0;
rep(i,1,n){
rep(j,1,m){
if(a[i][j]=='#')continue;
if(i){
vis[i][j]|=vis[i-1][j];
}
if(j){
vis[i][j]|=vis[i][j-1];
}
if(vis[i][j]){
ans=max(ans,i-1+j-1);
}
}
}
cout<<ans+1;
}
E
组合数学/计数dp
开始在一个点,每次可以移动到同行同列的任意位置,问移动k次后在另一个点的方案数
地图小的话可以dp记录走了k步,在每个位置的方案数。但是地图很大,那么从另一个角度来看,我们可以把所有点看成,初始点,和初始点同一行,和初始点同一列,和初始点不同行不同列,我们维护走了k步后在这四个区域的方案数是很简单的,复杂度也不大。并且其实每一个区域里的点的方案数都是一样的,都等于我们dp出来的值,因为我们dp是并没有指定到底在区域里的哪个点,只要在区域里就行。
所以最后看一下终点在哪个区域,输出对应方案即可
void solve(void){
int n,m,k;
cin>>n>>m>>k;
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
vvi dp(4,vi(k+1));
dp[0][0]=1;
rep(i,1,k){
dp[0][i]=(dp[1][i-1]*(m-1)+dp[2][i-1]*(n-1))%M2;
dp[1][i]=(dp[0][i-1]+dp[3][i-1]*(n-1)+dp[1][i-1]*(m-2))%M2;
dp[2][i]=(dp[0][i-1]+dp[3][i-1]*(m-1)+dp[2][i-1]*(n-2))%M2;
dp[3][i]=(dp[1][i-1]+dp[2][i-1]+dp[3][i-1]*(n+m-4))%M2;
// rep(j,0,3){
// cout<<dp[j][i]<<' ';
// }
// cout<<'\n';
}
if(x1==x2&&y1==y2)cout<<dp[0][k];
else if(x1==x2)cout<<dp[1][k];
else if(y1==y2)cout<<dp[2][k];
else cout<<dp[3][k];
}
F
状压dp 逆序对
两个等长序列,可以把一个元素加减一,代价x,可以交换一个序列中两个元素位置,代价y。问把两个序列变成一样的最小代价
注意到 n = 18 n=18 n=18,那么可以状压。考虑状压,假设mask有i个1,b序列不动,a序列选出mask里为1的这些元素,与b序列的的前i个元素对应,的最小代价。这里其实是利用了mask里的1的个数等于已选中元素个数,这样就能知道当前要选的是第几个元素,要和b序列中哪个元素对应
a中选一个未选中的元素和b中第i个元素对应,最后他们要相等,因此要用操作一把他们大小调到相等。并且要把这个元素从它在a序列中的原本位置,移动到当前位置,需要计算移动次数。
这个移动次数不太好想,开始我就是卡在这里了,因为你移动之后还会改变其他元素的位置,而我们要的是总移动次数最少。如果只考虑当前元素移动次数尽量少,可能总次数不一定最少。
这里要用到一个结论:每次只交换相邻元素的话,把一个排列升序排序所需的交换次数,就是初始的总逆序对个数。因为排序时,每个元素至少都要和所有与他构成逆序对的元素交换位置,这是交换次数的下界,不可能再少了。同时这个下界是可以取到的,在没排序完时,任意时刻总有两个相邻元素是逆序的,我们每次都交换相邻逆序对,这样对于每个逆序对,我们都只花了一次操作交换他们,总的操作次数就是逆序对个数。
回到本题,我们只要在dp过程中,计算逆序对个数,就是操作y的最小执行次数。然后这里的逆序对是指,在初始a中在当前元素后面,但是在选择方案中在当前元素前面。我们每次添加一个元素时,检查mask里有多少个序号比他大的即可
void solve(void){
int n,x,y;
cin>>n>>x>>y;
vi dp(1<<n,1e18);
dp[0]=0;
vi a(n+1),b(n+1);
rep(i,1,n){
cin>>a[i];
}
rep(i,1,n){
cin>>b[i];
}
rep(i,0,(1<<n)-1){
int cnt=__builtin_popcount(i);
rep(j,0,n-1){
if(i>>j&1)continue;
int rev=0;
rep(k,0,n-1){
if(i>>k&1){
if(k>j){
rev++;
}
}
}
dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+rev*y+x*abs(a[j+1]-b[cnt+1]));
}
}
cout<<dp[(1<<n)-1];
}