POJ 3254 Corn Fields
题意:
一块n*m的田,1表示这个地方可以种植,0代表这个地方不能种植。植物种植还必须满足两株植物不能相邻(横竖都不行)。问共有几种种植方法,而且当什么都不种时认为是一种方法。
解题思路:
种植用1表示,不种植用0表示。每一行的情况就可以用一个二进制数state来存储。state的范围是 [0 ~ 1<< state).
dp[i][state]表示第i行状态为state的情况下满足条件的数目。
状态转移方程为:dp[i][state] += dp[i-1][pre_state];这个state和pre_state必须满足意义所给的条件,即左右不相邻,上下不相邻。那么 第i行状态为state的情况为第i-1行所有满足条件的状态为pre_state相加而成。
最后的答案为最后一行所有状态的情况和相加而得。
位运算需要注意的地方:
1.注意打括号
2. 1 & (state>>i) 和 state & ( 1<< i ) 还是有差别的。 前者的答案只有0和1,而后者的答案有0和可能的正数。判断的时候还是要注意下写法
3. 当前行是否相邻的判断条件是: state & (state<<1) 是否为 0
4. 上下两行是否相邻的判断条件是: state & pre_state 是否为 0
#include
#include
#include
#include
using namespace std;
int dp[15][1<<15];
bool is[15][15];
int n,m;
const int MOD = 1e8;
bool check(int x,int state)
{
if( (state & (state<<1)) ) return false;
for(int i=1;i<=m;i++)
{
if( !is[x][i])
{
if( ((1<<(m-i)) & state) !=0 ) return false;
}
}
return true;
}
int main()
{
while( scanf("%d%d",&n,&m)!=EOF)
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++) for(int j = 1;j<=m;j++) scanf("%d",&is[i][j]);
dp[0][0] = 1;
long long ans = 0;
for(int i=1;i<=n;i++)
{
for(int j = 0; j < (1<if(check(i,j)) {//判断当前行满足条件的state
for(int k = 0; k < (1<//枚举上一行的pre_state进行更新
if( !(k&j) ) dp[i][j] += dp[i-1][k];
}
}
if(i==n) ans = (ans + dp[i][j]) % MOD;
}
}
printf("%lld\n",ans);
}
return 0;
}
POJ 1185 炮兵阵地
解题思路:
三维DP数组来表示状态的转移。
dp[i][j][k]表示在第i行状态为j且第x-1行状态为k时的方案数。因为第i行放炮兵与第i-1行和i-2行相关。
这里有个优化:对于每一行的状态都有 1< < m 种可能,如果每个枚举会TLE。其实每一行的最多状态数也只有60种。因为m最大为10,且每3个不能相邻,可枚举出来这60种情况。
#include
#include
#include
#include
#include
using namespace std;
bool is[150][15];
int dp[110][1<<11][1<<11];
int n,m,t=0;
int can[150];
void Init()
{
for(int i=0;i<(1<<10);i++)
{
if((i&(i<<1)) || (i&(i<<2))) continue;
can[++t] = i;
}
return;
}
int check(int x,int state)
{
int cnt = 0;
if( state >= pow((int)2,m) ) return -1;
for(int i=1;i<=m;i++)
{
if( ((1<<(i-1)) & state )) cnt++;
if(!is[x][i])
{
if( ((1<<(m-i)) & state)) return -1;
}
}
return cnt;
}
int main()
{
//freopen("duipai.out","r",stdin);
Init(); scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char op; scanf(" %c",&op);
if( op =='P') is[i][j] = true;
else is[i][j] = false;
}
int ans = 0,now;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=t;j++)
{
int cnt = check(i,can[j]);
if( cnt!=-1 ){
//cout<
for(int k = 1; k<=t;k++)
{
if( !(can[k]&can[j]) ){
dp[i][can[j]][can[k]] = cnt;
now = 0;
for(int q = 1;q<=t;q++)
{
if(i==1) ;
else {
if(!(can[q]&can[j])) now = max(now,dp[i-1][can[k]][can[q]]);
}
}
}
dp[i][can[j]][can[k]] += now;
if(i==n) ans = max(ans,dp[i][can[j]][can[k]]);
}
}
}
}
printf("%d\n",ans);
return 0;
}
POJ 3311 Hie with the Pie
题意:
一个送外卖的人,要将外卖全部送去所有地点再回到店离,求最短路。
解题思路:
旅行商问题。先用floyed得到每两个点之间的距离,然后状压dp解决旅行商问题。
类似题目:http://blog.csdn.net/codeblocksm/article/details/48210671
#include
#include
#include
#include
using namespace std;
int path[15][15];
const int INF = 0x3f3f3f;
int dp[15][1<<15];
int n;
void floyed()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
path[i][j] = min(path[i][j],path[i][k]+path[k][j]);
return ;
}
int main()
{
while( scanf("%d",&n)!=EOF && n )
{
n++;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>path[i][j];
floyed();
memset(dp,INF,sizeof(dp));
dp[1][1] = 0;
for(int i=1; i<(1<for(int j=1;j<=n;j++)
{
if(((i>>(j-1))&1)!=0 ){
for(int k=1;k<=n;k++)
{
if(((i>>(k-1))&1)==0 ) dp[k][i|(1<<(k-1))] = min(dp[k][i|(1<<(k-1))],dp[j][i]+path[j][k]);
}
}
}
}
int ans = INF;
for(int i=2;i<=n;i++)
{
ans = min(ans,dp[i][(1<1]+path[i][1]);
}
cout<return 0;
}
Codeforce 580D Kefa and Dishes
题意:
有n盘菜,每盘菜都有一个美味值。但是你只能选m盘菜。并且这些吃菜的顺序能影响总美味值。如果i当且仅当在j之前吃,那么会额外加一些美味值,这样的组合有k组。求选m盘菜吃能获得的最大的美味值。
解题思路:
dp[i][sta] 表示当前吃的菜是i,状态为sta的情况。
更新总答案的时候在更新sta的时候可以获得。
#include
using namespace std;
typedef long long LL;
LL dp[20][1<<18];
LL wei[20];
LL add[20][20];
int n,m,k;
bool check(int sta)
{
int now = 0;
for(int i=1;i<=n;i++)
{
if( sta & (1<<(i-1))) now++;
}
if( now == m) return true;
return false;
}
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++) scanf("%I64d",&wei[i]);
for(int i=1;i<=k;i++)
{
int u,v,w; cin>>u>>v>>w;
add[u][v] = w;
}
memset(dp,0,sizeof(dp));
LL ans = 0;
for(int i = 1; i<(1<for(int j = 1; j<=n;j++)
{
if( i&(1<<(j-1))) {
if( i == (1<<(j-1))) {
dp[j][i] = wei[j];
if(check(i)) ans = max(ans,dp[j][i]);//容易忽略的地方
}
for(int k = 1;k<=n;k++)
{
if( !( i&(1<<(k-1))))
{
int sta = i|(1<<(k-1));
dp[k][sta] = max( dp[k][sta],dp[j][i] + add[j][k] + wei[k]);
if(check(sta)) ans = max(ans,dp[k][sta]);//顺便更新当前的值
}
}
}
}
}
cout<return 0;
}