poj1038
题意:用2*3格子覆盖有禁区的n*m长方形,问最多可以放置多少个格子。
解法:整行覆盖,第二维存储前两行的覆盖状态
dp[i][st]:前i-1行已覆盖完成,且i-1,i-2行的覆盖状态为st,最多可以放置的格子数
dp[i][st]−−>dp[i+1][st′]
状态转移通过枚举第i行的覆盖状态完成。注意地图上禁区不能覆盖。
状态设计用三进制存储:
0:前两行均无覆盖
1:i-2行覆盖,i-1行无覆盖
2:i-1行覆盖
枚举时从第0列开始,分三种情况:
1.不填充该格
2.横放2*3格子,以该格为右上角
3.竖放2*3格子,以该格为右上角
然后进行dfs,直到枚举到最后一列,得到最终状态st’,进行状态转移即可
# include<iostream>
# include<cstdio>
# include<algorithm>
# include<queue>
# include<complex>
# include<stack>
# include<math.h>
# include<cstring>
#include<map>
#define ll long long
#define pi acos(-1)
#define MP make_pair
#define X first
#define Y second
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 150+10;
const int maxm = 10+10;
const int maxst = 60000;
int t,n,m;
int dp[3][maxst];
bool ma[maxn][maxm];
int bit[maxm];
int three[maxm],ans,cur;
void init(){
three[0]=1;
for(int i=1;i<=11;i++) three[i]=three[i-1]*3;
}
void fenjie(int x,int &y){
for(int i=0;i<m;i++){
bit[i]=x%3;
if(bit[i]!=0) y+=three[i]*(bit[i]-1);
x/=3;
}
}
bool d1(int r,int c){//横放地图限制
if(ma[r][c]||ma[r][c+1]||ma[r-1][c]||ma[r-1][c+1]||ma[r-2][c]||ma[r-2][c+1]) return false;
return true;
}
bool d2(int r,int c){//竖放地图限制
if(ma[r][c]||ma[r-1][c]||ma[r][c+1]||ma[r-1][c+1]||ma[r][c+2]||ma[r-1][c+2]) return false;
return true;
}
void update(int row,int nowc,int now,int num){
if(nowc>=m){
dp[cur][now]=max(dp[cur][now],num);
return;
}
if(row-2>=0&&nowc+1<m&&bit[nowc]==0&&bit[nowc+1]==0&&d1(row,nowc)){//可以横放
update(row,nowc+2,now+three[nowc]*2+three[nowc+1]*2,num+1);
}
if(row-1>=0&&nowc+2<m&&bit[nowc]!=2&&bit[nowc+1]!=2&&bit[nowc+2]!=2&&d2(row,nowc)){//可以竖放
update(row,nowc+3,now+three[nowc]*2+three[nowc+1]*2+three[nowc+2]*2,num+1);
}
update(row,nowc+1,now,num);//不放
}
int main(){
init();
//freopen("a.txt","r",stdin);
scanf("%d",&t);
while(t--){
int num;
scanf("%d%d%d",&n,&m,&num);
memset(ma,0,sizeof(ma));
for(int i=1;i<=num;i++){
int x,y;
scanf("%d%d",&x,&y); x--,y--;
ma[x][y]=1;
}
//第一列不放
for(int i=0;i<maxst-3;i++) dp[0][i]=-INF;
dp[0][0]=0; cur=0;
//从第二列开始放置
ans=0;
for(int i=1;i<n;i++){
cur^=1;
for(int j=0;j<maxst;j++) dp[cur][j]=-INF;
for(int j=0;j<three[m];j++){//注意枚举上界不再是(1<<M),不能使用位运算!
if(dp[1-cur][j]==-INF) continue;
int k=0;
fenjie(j,k);
dp[cur][k]=max(dp[cur][k],dp[1-cur][j]);//整行不放
update(i,0,k,dp[1-cur][j]);
}
}
for(int i=0;i<three[m];i++) ans=max(ans,dp[cur][i]);
printf("%d\n",ans);
}
return 0;
}
zoj1346
题意:DAG图拓扑排序的个数统计
解法:dp[st]表示还有st状态的点未加入序列时,还能够有的排序方法数
删除已加入的点及相关边,然后枚举当前入度为0的点,加入st状态,记忆化搜索即可
复杂度分析:
n2∗2n
# include<iostream>
# include<cstdio>
# include<algorithm>
# include<queue>
# include<complex>
# include<stack>
# include<math.h>
# include<cstring>
#include<map>
#define ll long long
#define pi acos(-1)
#define MP make_pair
#define X first
#define Y second
using namespace std;
const int maxn = (1<<18);
int n,cnt;
map<string,int> ma;
char ch1[20],ch2[20];
int du[20];
int vis[20][20];
ll dp[maxn];
ll dfs(int st){
if(dp[st]!=-1) return dp[st];
int tmpdu[20];
ll tmp=0;
for(int j=0;j<cnt;j++) tmpdu[j]=du[j];
for(int i=0;i<cnt;i++){//删点 减度
if(st&(1<<i)){
for(int j=0;j<cnt;j++) if(vis[i][j]){
tmpdu[j]--;
}
}
}
for(int j=0;j<cnt;j++) if(!(st&(1<<j))&&!tmpdu[j]){
tmp+=dfs(st|(1<<j));
}
return dp[st]=tmp;
}
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
ma.clear(); cnt=0;
memset(du,0,sizeof(du));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
scanf("%s%s",ch1,ch2);
if(ma.find(string(ch1))==ma.end()) ma[string(ch1)]=cnt++;
if(ma.find(string(ch2))==ma.end()) ma[string(ch2)]=cnt++;
int u=ma[string(ch1)],v=ma[string(ch2)];
vis[u][v]=1;
du[v]++;
}
memset(dp,-1,sizeof(dp));
dp[(1<<cnt)-1]=1;
dfs(0);
printf("%lld\n",dp[0]);
}
return 0;
}