Wc2008 游览计划(STNT + 输出路径)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2595

题目大意:给你n*m的矩阵,每个格子上的数字表示该地的权值,权值为0,表示该点为关键点,现在让你求至少包含这些关键点的STNT,然后一种可行方案。

思路:STNT,就是加了输出路径。

这里还要注意,由于我们是把权值放在节点上,所以枚举子树做DP时,还要减掉一个 该点的权值,即 d[ s ][ i ] = min(d[ s ][ s1 ]+ d[ s2 ][ s - s1 ] - mat[ i ]),s1为s的子集。

inq[ s ][ i ] 一直写成 inq[ i ] ,调了好久,编译器还不报错。。 = =

还有边先开始只开了 MAXN*MAXN*2,竟然不报 RE,报 WA,这个调得更久,怪不得一直 WA 呢。。。T^T

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff ;

const int MAXN = 11;

struct Edge
{
    int t,next,w;
} edge[MAXN*MAXN*4];

int tot,head[MAXN*MAXN];

void add_edge(int s,int t,int w)
{
    edge[tot].w = w;
    edge[tot].t = t;
    edge[tot].next = head[s];
    head[s] = tot++;
}

char map[MAXN][MAXN];

int d[1<<11][MAXN*MAXN];

int val[MAXN*MAXN];

vector <int> spot;

bool inq[1<<11][MAXN*MAXN];

struct Node
{
    int s,id;
    Node(){}
    Node(int a,int b) : s(a),id(b) {}
};

Node path[1<<11][MAXN*MAXN];

queue <Node> q;

void init(int n,int k)
{
    int S = 1<<k;
    for(int s = 0;s < S;s++)
        for(int i = 0;i<n;i++)
            d[s][i] = INF;

    for(int i = 0;i<spot.size();i++)
    {
        int cur = spot[i];
        d[val[cur]][cur] = 0;
        path[val[cur]][cur].id = -1;
    }
}

int update(int s,int id,int w,int from_s,int from_id)
{
    if(d[s][id] > w)
    {
        d[s][id] = w;
        path[s][id].s = from_s;
        path[s][id].id = from_id;
        return 1;
    }
    else return 0;
}

void spfa()
{
    while(!q.empty())
    {
        int s = q.front().s;
        int id = q.front().id;
        inq[s][id] = 0;
        q.pop();
        for(int e = head[id]; e != -1;e = edge[e].next)
        {
            int v = edge[e].t;
            int w = edge[e].w;
            if(update(s|val[v],v,d[s][id] + w,s,id) && s|val[v] == s && !inq[s][v])
            {
                inq[s][v] = 1;
                q.push(Node(s,v));
            }
        }
    }
}

int p[MAXN*MAXN];

void stnt(int n,int k)
{
    init(n,k);
    int S = 1<<k;
    for(int s = 1;s<S;s++)
    {
        for(int i = 0;i<n;i++)
        {
            if(val[i] && !(val[i]&s)) continue;
            for(int s1 = (s - 1)&s ; s1 ;s1 = (s1 - 1)&s)
            {
                int s2 = s - s1;
                int tmp = d[s1|val[i]][i] + d[s2|val[i]][i] - p[i] ;
                if(d[s][i] > tmp)
                {
                    d[s][i] = tmp;
                    path[s][i].s = s1|val[i];
                    path[s][i].id = i;
                }
            }
            if(d[s][i] < INF) q.push(Node(s,i)),inq[s][i] = 1;
        }
		spfa();
        //for(int i = 0;i<n;i++)
            //printf("s = %d,i = %d,d = %d\n",s,i,d[s][i]);
    }
}

void print(int s,int id,int n,int m)
{
    if(val[id] == 0)
    {
        map[id/m][id%m] = 'o';
    }
    if(path[s][id].id == -1) return ;
    if(path[s][id].id == id)
    {

        print(path[s][id].s ,id,n,m);
        print((s - path[s][id].s)|val[id] ,id,n,m);//注意这里的状态转移
    }
    else
    {
        print(path[s][id].s,path[s][id].id,n,m);
    }
}

int main()
{
    //freopen("D://out.txt","w",stdout);
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        tot=0;
        memset(head,-1,sizeof(head));

        int cc = 0;
        spot.clear();
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                int cur = i*m+j;
                val[cur] = 0;
                int a;
                scanf("%d",&a);
                p[cur] = a;
                if(i >= 1) add_edge(cur - m,cur,a);
                if(i <= n - 2) add_edge(cur + m,cur,a);
                if(j >= 1) add_edge(cur - 1,cur,a);
                if(j <= m - 2) add_edge(cur + 1,cur,a);
                if(a == 0)
                {
                    map[i][j] = 'x';
                    val[cur] = 1<<cc;
                    spot.push_back(cur);
                    cc ++;
                }
                else map[i][j] = '_';
            }
            map[i][m] = '\0';
        }
        int nn = n*m;

        stnt(nn,cc);

        int S = 1<<cc;
        int x;
        int minn = INF;
        for(int i =0;i<nn;i++)
            if(d[S - 1][i] < minn)
            {
                minn = d[S - 1][i];
                x = i;
            }
        printf("%d\n",minn);
        print(S - 1,x,n,m);
        for(int i = 0;i<n;i++)
            puts(map[i]);
    }
    return 0;
}

/*

3 5
2 0 1 2 3
3 0 3 1 2
0 2 4 1 0

8 8
1 4 1 3 4 2 4 1
4 3 1 2 0 1 2 3
3 2 1 3 0 3 1 2
2 6 5 0 2 4 1 0
5 1 2 1 3 4 2 5
5 1 3 1 5 0 1 4
5 0 6 1 4 5 3 4
0 2 2 2 3 4 1 1

6 7
3 1 2 0 1 2 3
2 1 3 0 3 1 2
6 5 0 2 4 1 0
1 2 1 3 4 2 5
1 3 1 5 0 1 4
0 6 1 4 5 3 4

4 4
0 3 1 2
2 4 1 0
3 4 2 5
5 0 1 4

*/


当然不建边,转移时直接往四个方向走也可以,代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff ;

const int MAXN = 11;

int dir_x[] = {0,0,-1,1};
int dir_y[] = {-1,1,0,0};

char map[MAXN][MAXN];

int mat[MAXN][MAXN];

int d[1<<11][MAXN][MAXN];

int val[MAXN][MAXN];

vector <int> spot;

bool inq[1<<11][MAXN][MAXN];

struct Node
{
    int s,x,y;
    Node(){}
    Node(int a,int b,int c) : s(a),x(b),y(c) {}
};

Node path[1<<11][MAXN][MAXN];

queue <Node> q;

void init(int n,int m,int k)
{
    int S = 1<<k;
    for(int s = 0;s < S;s++)
        for(int i = 0;i<n;i++)
            for(int j = 0;j<m;j++)
                d[s][i][j] = INF;
    for(int i = 0;i<n;i++)
        for(int j = 0;j<m;j++)
        {
            d[val[i][j]][i][j] = 0;
            path[val[i][j]][i][j].s = 0;
        }
}

void spfa(int n,int m)
{
    while(!q.empty())
    {
        int s = q.front().s;
        int x = q.front().x;
        int y = q.front().y;
        inq[s][x][y] = 0;
        q.pop();
        for(int dd = 0; dd<4;dd ++)
        {
            int xx = x + dir_x[dd];
            int yy = y + dir_y[dd];
            if(xx < 0 || xx >=n || yy < 0 || yy >=m) continue;
            int to = s|val[xx][yy];
            int ok = 0;
            if(d[to][xx][yy] > d[s][x][y] + mat[xx][yy])
            {
                d[to][xx][yy] = d[s][x][y] + mat[xx][yy];
                path[to][xx][yy].s = s;
                path[to][xx][yy].x = x;
                path[to][xx][yy].y = y;
                ok = 1;
            }
            if(ok && !val[xx][yy] && !inq[s][xx][yy])
            {
                inq[s][xx][yy] = 1;
                q.push(Node(s,xx,yy));
            }
        }
    }
}

void stnt(int n,int m,int k)
{
    init(n,m,k);
    int S = 1<<k;
    for(int s = 1;s<S;s++)
    {
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                if(val[i][j] && !(val[i][j]&s)) continue;
                for(int s1 = (s - 1)&s ; s1 ;s1 = (s1 - 1)&s)
                {
                    int s2 = s - s1;
                    int tmp = d[s1|val[i][j]][i][j] + d[s2|val[i][j]][i][j] - mat[i][j] ;
                    if(d[s][i][j] > tmp)
                    {
                        d[s][i][j] = tmp;
                        path[s][i][j].s = s1|val[i][j];
                        path[s][i][j].x = i;
                        path[s][i][j].y = j;
                    }
                }
                if(d[s][i][j] < INF) q.push(Node(s,i,j)),inq[s][i][j] = 1;
            }
        }
        spfa(n,m);
        /*for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
                printf("s = %d,i = %d,j = %d,d = %d\n",s,i,j,d[s][i][j]);
            puts("");
        }*/
    }
}

void print(int s,int x,int y)
{
    //printf("s = %d,x = %d,y = %d,p.s = %d\n",s,x,y,path[s][x][y].s);
    if(val[x][y] == 0)
    {
        map[x][y] = 'o';
    }
    if(!path[s][x][y].s) return ;
    if(path[s][x][y].x == x && path[s][x][y].y == y)
    {

        print(path[s][x][y].s ,path[s][x][y].x,path[s][x][y].y);
        print((s - path[s][x][y].s)|val[x][y],path[s][x][y].x,path[s][x][y].y);
    }
    else
    {
        print(path[s][x][y].s,path[s][x][y].x,path[s][x][y].y);
    }
}

int main()
{
    //freopen("D://out.txt","w",stdout);
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        int cc = 0;
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                scanf("%d",&mat[i][j]);
                val[i][j] = 0;
                if(mat[i][j] == 0)
                {
                    map[i][j] = 'x';
                    val[i][j] = 1<<cc;
                    cc ++;
                }
                else map[i][j] = '_';
            }
            map[i][m] = '\0';
        }
        stnt(n,m,cc);
        int S = 1<<cc;
        int x,y;
        int minn = INF;
        for(int i =0;i<n;i++)
            for(int j = 0;j<m;j++)
                if(d[S - 1][i][j] < minn)
                {
                    minn = d[S - 1][i][j];
                    x = i;
                    y = j;
                }
        printf("%d\n",minn);
        print(S - 1,x,y);
        for(int i = 0;i<n;i++)
            puts(map[i]);
    }
    return 0;
}

/*

4 4
0 1 1 0
2 5 5 1
1 5 5 1
0 1 1 0

3 5
2 0 1 2 3
3 0 3 1 2
0 2 4 1 0

8 8
1 4 1 3 4 2 4 1
4 3 1 2 0 1 2 3
3 2 1 3 0 3 1 2
2 6 5 0 2 4 1 0
5 1 2 1 3 4 2 5
5 1 3 1 5 0 1 4
5 0 6 1 4 5 3 4
0 2 2 2 3 4 1 1

6 7
3 1 2 0 1 2 3
2 1 3 0 3 1 2
6 5 0 2 4 1 0
1 2 1 3 4 2 5
1 3 1 5 0 1 4
0 6 1 4 5 3 4

4 4
0 3 1 2
2 4 1 0
3 4 2 5
5 0 1 4

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值