标签:
状态压缩DP:
1、矩阵类型:
枚举当前行的所有状态状态,加限定条件。
枚举上一行的所有状态,加限定条件。
......
找出递推的方程,
基本方法:枚举每一行状态,由上向下一行一行递推。
经典例题:
# include <iostream>
# include <cstring>
# include <cmath>
using namespace std;
int map[175][175];
int cl[4096];// 所有合法状态
int num[4096];
int dp[102][202][202];
int n, m;
int a[4096] = {0};
// 判断某个状态
bool ok(int i)
{
if((i&(i<<2))==0 && (i&(i>>2))==0)
return true;
return false;
}
bool judge(int x, int y)
{
if((x&y) == 0)
return true;
return false;
}
bool judge1(int x, int y)
{
if(((x<<1)&y) != 0 || ((x>>1)&y)!=0)
return false;
return true;
}
int shu(int x)//计算x的二进制中1的个数
{
int ret=0;
while(x)
{
if(x&1)ret++;
x>>=1;
}
return ret;
}
bool vis(int i, int t)
{
if((a[i]&t) != t)
return false;
return true;
}
int main()
{
while(cin >> n >> m)
{
if(n < 0 || m < 0)
continue;
// 输入处理
memset(map, 0, sizeof(map));
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; i++)
{
a[i] = 0;
for(int j = 1; j <= m; j++)
{
cin >> map[i][j];
if(map[i][j])
a[i] += pow(2, j - 1);
}
}
int jishu = 0;
for(int i = 0; i < (1<<m); i++)
{
if(ok(i))
{
cl[jishu] = i;
num[jishu++] = shu(i);
}
}
memset(dp, -1, sizeof(dp));
for(int i = 0; i < jishu; i++)
if(vis(1, cl[i]))
dp[1][i][0] = num[i];
for(int i = 2; i<= n; i++)
{
for(int j = 0; j < jishu; j++) //i
{
if(!vis(i, cl[j]))
continue;
for(int k = 0; k < jishu; k++)// i - 1
{
if(!judge1(cl[j], cl[k]))
continue;
if(!vis(i - 1, cl[k]))
continue;
for(int x = 0; x < jishu; x++) // i - 2
{
if(!vis(i - 2, cl[x]))
continue;
if(!judge1(cl[k], cl[x]) || !judge(cl[j], cl[x]) )
continue;
dp[i][j][k] = max(dp[i][j][k], dp[i - 1][k][x] + num[j]);
}
}
}
}
int ret = 0;
for(int i = 0; i < jishu; i++)
{
for(int j = 0; j < jishu; j++)
{
if(dp[n][i][j] > ret)
ret = dp[n][i][j];
}
}
cout << ret << endl;
}
return 0;
}
2、线性类型
从某一开始状态到达某一状态
枚举所有状态(从最初状态开始);
遍历所有线性的项,判断此状态有没有包含该项,if(yes),,,else
基本方法:由前一状态推导后一状态,判断条件等
例题:
# include <iostream>
# include <string>
# include <cstring>
using namespace std;
const int MAX = 1<<16;
const int INF = 0x3f3f3f3f;
struct sub
{
string name;
int date;
int need;
}a[MAX];
bool vis[MAX];
struct DP
{
int cost;
int pre;
int core;
}dp[MAX];
void output(int now)
{
int next = dp[now].pre ^ now;
int id = 0;
next >>= 1;
while(next)
{
id++;
next >>= 1;
}
if(dp[now].pre != 0)
{
output(dp[now].pre);
}
cout << a[id].name << endl;
}
int main()
{
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> a[i].name >> a[i].date >> a[i].need;
}
memset(vis, false, sizeof(false));
for(int i = 0; i < MAX; i++)
{
dp[i].core = INF;
}
dp[0].cost = 0;
dp[0].pre = -1;
dp[0].core = 0;
vis[0] = true;
for(int i = 0; i < (1<<n); i++)
{
for(int j = 0; j < n; j++)
{
if((i&(1<<j)) == 0)
{
int next = i|(1<<j);
dp[next].cost = dp[i].cost + a[j].need;
int Min = dp[next].cost - a[j].date;
if(Min < 0)
Min = 0;
Min += dp[i].core;
if(vis[next])
{
if(Min < dp[next].core)
{
dp[next].core = Min;
dp[next].pre = i;
}
}
else
{
vis[next] = true;
dp[next].core = Min;
dp[next].pre = i;
}
}
}
}
cout << dp[(1<<n) - 1].core << endl;
output((1<<n)-1);
}
return 0;
}
3、TSP 问题
通常情况下,需要用Floyd算法预处理一下,
然后再枚举状态,枚举到达的地方 判断某状态下到达本城市的最优方案
经典例题:
# include <iostream>
# include <cstdio>
# include <cstring>
const int INF = 0x3f3f3f3f;
using namespace std;
int dis[155][155];
int dp[1<<16][16];
int num[22];
int earn[22];
int cost[22];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n, m, mon;
scanf("%d%d%d", &n, &m, &mon);
memset(dis, INF, sizeof(dis));
for(int i = 1; i <= n; i++)
dis[i][i] = 0;
int a, b, c;
while(m--)
{
scanf("%d%d%d", &a, &b, &c);
if(c < dis[a][b])
dis[a][b] = dis[b][a] = c;
}
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
if(dis[i][k] != INF)
for(int j = 1; j <= n; j++)
if(dis[k][j] != INF && dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
memset(dp, -1, sizeof(dp));
int h, tmp;
scanf("%d", &h);
for(int i = 0; i < h; i++)
scanf("%d%d%d", &num[i], &earn[i], &cost[i]);
for(int i = 0; i < h; i++)
{
tmp = mon - dis[1][num[i]] - cost[i];
if(tmp >= 0)
dp[1<<i][i] = tmp + earn[i];
}
int st = (1 << h) - 1;
for(int i = 1; i <= st; i++)
for(int j = 0; j < h; j++)
{
if(dp[i][j] < 0)
continue;
for(int k = 0; k < h; k++)
{
if(i & (1<<k))
continue;
tmp = dp[i][j] - dis[num[j]][num[k]] - cost[k];
if(tmp >= 0)
{
tmp += earn[k];
int stat = i + (1<<k);
dp[stat][k] = max(dp[stat][k], tmp);
}
}
}
int flag = 0;
for(int i = 0; i < h; i++)
{
tmp = dp[st][i] - dis[num[i]][1];
if(tmp >= 0)
{
flag = 1;
break;
}
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
**双调旅行商问题
这题还没理解透............
# include <iostream>
# include "cstdio"
# include "cstring"
# include "algorithm"
# include "cmath"
using namespace std;
int N;
struct NODE
{
double x;
double y;
}p[205];
double dis[205][205], dp[205][205];
void solve()
{
dp[1][2] = dis[1][2];
for(int j = 3; j <= N; j++)
{
for(int i = 1; i < j - 1; i++)
{
dp[i][j] = dp[i][j - 1] + dis[j - 1][j];
}
dp[j - 1][j] = 999999999;
for(int i = 1; i < j - 1; i++)
{
dp[j - 1][j] = min(dp[j - 1][j], dp[i][j - 1] + dis[i][j]);
}
}
dp[N][N] = dp[N-1][N] + dis[N-1][N];
}
int main()
{
while(~scanf("%d",&N))
{
for(int i = 1; i <= N; i++)
scanf("%lf %lf",&p[i].x, &p[i].y);
for(int i = 1; i <= N; i++)
for(int j = i + 1; j <= N;j++)
dis[i][j] = sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
solve();
printf("%.2lf\n",dp[N][N]);
}
}
综上为这几天做题小结(持续更新中........)
标签:
原文地址:http://www.cnblogs.com/lyf-acm/p/5746129.html