继续学习状态压缩的相关知识。
本来准备继续按照上篇博文里提到的那篇论文继续学习,但被矩形完全覆盖虐了回来,决定先做些其他的题增进理解之后再回来做。
Zoj 3471 Most Powerful
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3471
题意:不超过10种原子,两两之间相互碰撞可以产生一定的能量,如a碰b,那么b原子就消失,自身不能碰自身,问最后所能得到的最大能量。
#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))
int a[11][11];
int dp[1<<10];
int main ()
{
int n,i,j;
while (scanf("%d",&n),n)
{
for (i=0;i<n;i++)
for (j=0;j<n;j++)
scanf("%d",&a[i][j]);
memset(dp,0,sizeof(dp));
for (int s=(1<<n)-1;s>=0;s--) //初始时全部存在
for (i=0;i<n;i++)
if (s&(1<<i))
for (j=0;j<n;j++)
{
if (i==j)
continue;
if (s&(1<<j))
{
int now=s-(1<<j); //原子j被i消耗
dp[now]=max(dp[now],dp[s]+a[i][j]);
}
}
int ans=0;
for (i=0;i<(1<<n);i++)
ans=max(ans,dp[i]);
printf("%d\n",ans);
}
return 0;
}
Hdu 4539 郑厂长系列故事——排兵布阵
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4539
思路:和炮兵阵地那个题差不多,使用了滚动数组
要是做腾讯比赛的时候我就学过状态压缩dp该多好……
#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))
int n,m;
int top,num[173];
int stk[173],can[103];
int dp[2][173][173];
bool Ok (int x)
{
if (x&(x<<2))
return false;
return true;
}
//计算一个整型数x的二进制中1的个数
int Cal (int x)
{
int cnt=0;
while (x)
{
cnt++;
x&=(x-1);
}
return cnt;
}
//找到所有可能的合法状态,170种左右
void Init ()
{
top=0;
int total=1<<m;
for (int i=0;i<total;i++)
if (Ok(i))
{
stk[++top]=i;
num[top]=Cal(stk[top]);
}
memset(dp,0,sizeof(dp));
memset(can,0,sizeof(can));
}
int main ()
{
#ifdef ONLINE_JUDGE
#else
freopen("read.txt","r",stdin);
#endif
while (~scanf("%d%d",&n,&m))
{
Init ();
int i,j,k,t,ans=0;
for (i=1;i<=n;i++)
{
int temp=0,x;
for (j=1;j<=m;j++)
{
scanf("%d",&x);
temp=temp*2+x;
}
can[i]=temp; //能放的地方
}
for (i=1;i<=top;i++)
if ((stk[i]|can[1])==can[1])
dp[1][i][1]=num[i];
for (i=2;i<=n;i++)
{
int a=i&1;
memset(dp[a],0,sizeof(dp[a]));
for (j=1;j<=top;j++)
if ((stk[j]|can[i])==can[i])
for (k=1;k<=top;k++)
{
if ((stk[j]&(stk[k]<<1)) || ((stk[j]<<1)&stk[k]))
continue;
if ((stk[k]|can[i-1])!=can[i-1])
continue;
for (t=1;t<=top;t++)
{
if (stk[j]&stk[t])
continue;
if (i>=3 && (stk[t]|can[i-2])!=can[i-2])
continue;
dp[a][j][k]=max(dp[a][j][k],dp[a^1][k][t]+num[j]);
}
}
}
for (i=1;i<=top;i++)
for (j=1;j<=top;j++)
ans = max(ans,dp[n&1][i][j]);
printf("%d\n",ans);
}
return 0;
}
Hdu 1429 胜利大逃亡(续)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1429
思路:这个题不是dp,是状态压缩+Bfs,算是状态压缩的应用吧。
#include <cstdio>
#include <cstring>
#include <queue>
#include <cctype>
using namespace std;
const int limit=1<<10;
const int N=21;
int n,m,all;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
struct Point
{
int x,y;
int step,key;
void Get (int _x,int _y,int _step,int _key)
{
x=_x;
y=_y;
step=_step;
key=_key;
}
}now,temp;
char g[N][N];
bool visit[limit][N][N];
int Change (char c)
{
return c-(isupper(c)?'A':'a');
}
int OK (int x,int y,int key)
{
if (x<0 || x>=n || y<0 || y>=m)
return false;
if (g[x][y]=='*')
return false;
if (visit[key][x][y])
return false;
return true;
}
int Bfs (int sx,int sy)
{
queue <Point> q;
visit[0][sx][sy]=true;
temp.Get (sx,sy,0,0);
q.push(temp);
while (!q.empty())
{
Point now=q.front();
q.pop();
if (now.step>=all) //剪枝
break;
if (g[now.x][now.y]=='^')
return now.step;
for (int i=0;i<4;i++)
{
int x=now.x+dx[i];
int y=now.y+dy[i];
int t=now.step+1;
int key=now.key;
if (OK(x,y,key)==false)
continue;
if (isupper(g[x][y]) && (key & (1<<Change(g[x][y])))==0) //没有钥匙
continue;
if (islower(g[x][y])) //得到钥匙
key|=(1<<Change(g[x][y]));
visit[key][x][y]=true;
temp.Get (x,y,t,key);
q.push(temp);
}
}
return -1;
}
int main ()
{
while (~scanf("%d%d%d",&n,&m,&all))
{
memset(visit,false,sizeof(visit));
int sx,sy;
for (int i=0;i<n;i++)
{
scanf("%s",g[i]);
for (int j=0;j<m;j++)
if (g[i][j]=='@')
sx=i,sy=j;
}
printf("%d\n",Bfs(sx,sy));
}
return 0;
}
Hdu 1074 Doing Homework
这个题没能独立完成…………
题意和思路可以参考这里:http://www.cnblogs.com/AndreMouche/archive/2011/01/28/1946997.html
#include <cstdio>
#include <cstring>
const int N =1<<16;
struct Node
{
int cost;//所需要的时间
int pre;//前一状态
int reduced;//最少损失的分数
}dp[N];
bool visit[N];//表示完成j的状态是否被访问
int n;
struct Data
{
int deadtime;//截止日期
int cost;//所需日期
char name[105];
void Get ()
{
scanf("%s%d%d",name,&deadtime,&cost);
}
}data[16];
void Output (int status)//递归输出课程安排表
{
int curjob= dp[status].pre^status;
int id=0;
curjob>>=1;
while (curjob)
id++,curjob>>=1;
if (dp[status].pre!=0) //输出其前面的课程
Output (dp[status].pre);
printf("%s\n",data[id].name);
}
void Input ()
{
scanf("%d",&n);
for (int i=0;i<n;i++)
data[i].Get ();
memset(visit,false,sizeof(visit));
memset(dp,0,sizeof(dp));
}
int main ()
{
int T;
scanf("%d",&T);
while (T--)
{
Input ();
int limit=(1<<n)-1;
for (int i=0;i<=limit;i++)
for (int work=0;work<n;work++)
{
int cur=1<<work;
if ((cur & i)==0)//该项作业尚未做过
{
int last=cur|i; //做该项作业之后的状态
int day=dp[i].cost+data[work].cost;
dp[last].cost=day;
int reduce = day-data[work].deadtime;
if (reduce<0)
reduce=0;
reduce+=dp[i].reduced; //做完后超过的时间
if (visit[last])//该状态已有访问信息
{
if (reduce<dp[last].reduced)
{
dp[last].reduced=reduce;
dp[last].pre=i;
}
/* else //扣分相同,取字典序小的那一个,由于这里j是按从小到达搜索的,默认已是按字典序,不需再处理
if (reduce==dp[last].reduced && dp[last].pre>i)
dp[last].pre=i;
*/
}
else
{
visit[last]=true;
dp[last].reduced=reduce;
dp[last].pre=i;
}
}
}
printf("%d\n",dp[limit].reduced);
Output (limit);
}
return 0;
}