先来一个题衔接一下:
与上一题的思路差不多,不过这里有几点需要注意:
1.因为某一列的状态还与上上一行有关,因此我们令f[i][j][k]表示第i行状态为j,第i-1行状态为k的最大炮兵数。
因此,我们可以得到状态转移方程:f[i][j][k]=max(f[i][j][k],f[i-1][k][q]+num[j])其中我们保证j,k,q不冲突并且自己可以。
2.注意到直接开存不下,我们考虑用vector存符合条件的,并计算一下有几个再开空间。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[110],dp[110][70][70];
vector<int> st;
char b;
vector<int> num;
int calc(int num){
int ans=0;
while(num){
if((num&1)==1) ans++;
num>>=1;
}
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf(" %c",&b);
if(b=='H'){
a[i]|=(1<<(j-1));
}
}
}
for(int i=0;i<=(1<<m)-1;i++){
if(i&(i<<1)) continue;
if(i&(i<<2)) continue;
st.push_back(i);
num.push_back(calc(i));
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<st.size();j++){
if((a[i]&st[j])) continue;
for(int k=0;k<st.size();k++){
if((a[i-1]&st[k])) continue;
if((st[k]&st[j])) continue;
for(int q=0;q<st.size();q++){
if(i>=2){
if((a[i-2]&st[q])) continue;}
if((st[k]&st[q])) continue;
if((st[q]&st[j])) continue;
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][q]+num[j]);
ans=max(ans,dp[i][j][k]);
}
}
}
}
cout<<ans;
}
首先,什么是TSP问题?
即给你一张抽象的图,求从某一个起点出发,经过所有点的最短路径。
如何解决呢?
我们先建立一个超级源点,这就解决了从某一个起点出发的问题,然后,我们假设走了134,现在在5,那么后来的267是与134的走法无关的,因此我们只要保留最短的即可,即DP。
因此,我们可以令f[st][i]表示当前状态为st,最后到达的一个点为i所经过的最短路径。
访问过标1,未访问标0.
转移方程为f[st][i]=min(f[st'][j]+a[j][i]).(st'=st-1<<(i-1)).
若为必须回到原点,那么走出来的一定是一个圈,因此我们固定1为起点,然后在原来的结果上加上终点与1的边。
下面是实现代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[25][25],dp[1<<20][25];
int f(int st,int x){
if(dp[st][x]<=1e9){
return dp[st][x];
}
int stt=st-(1<<(x-1));
for(int i=1;i<=n;i++){
if(a[i][x]==0) continue;
if((stt>>(i-1))&1){
dp[st][x]=min(dp[st][x],a[i][x]+f(stt,i));
}
}
return dp[st][x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
a[x][y]=z;
a[y][x]=z;
}
memset(dp,0x7f,sizeof(dp));
dp[1][1]=0;
int ans=0x7f7f7f7f;
int st=(1<<n)-1;
for(int i=2;i<=n;i++){
int tmp=f(st,i);
if(a[i][1]!=0) ans=min(ans,a[i][1]+tmp);
}
cout<<ans;
}
我们来看一个类似的问题:
思路类似,我们令f[i]表示状态为i时获得的最大能量。
其中第k位==1表示它已经用了并消失,为0表示没有用或用了没消失。
易得状态转移方程:f[k|(1<<(i-1)]=max(f[k]+a[j][i]).我们转换一下:
f[k]=max(f[k']+a[j][i])(其中k'的i与j位为0,k=k'+1<<(i-1))
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[14][14],x,f[2000];
int main(){
while(cin>>n){
memset(f,0,sizeof(f));
if(n==0) break;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&x);
a[i][j]=x;
}
}
int ans=0;
for(int i=1;i<=(1<<n)-2;i++){
for(int k=0;k<i;k++){
for(int ii=1;ii<=n;ii++){
if((k>>(ii-1))&1) continue;
for(int jj=1;jj<=n;jj++){
if((k>>(jj-1))&1) continue;
if(i!=(k|(1<<(jj-1)))) continue;
f[i]=max(f[i],f[k]+a[ii][jj]);
ans=max(ans,f[i]);
}
}
}
}
printf("%d\n",ans);
}
}