码迷,mamicode.com
首页 > 其他好文 > 详细

动态规划总结

时间:2016-08-07 15:17:39      阅读:235      评论:0      收藏:0      [点我收藏+]

标签:

状态压缩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、线性类型

从某一开始状态到达某一状态

枚举所有状态(从最初状态开始);

遍历所有线性的项,判断此状态有没有包含该项,ifyes,,,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;

}

3TSP 问题

通常情况下,需要用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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!