HDU 5711 Ingress(状压dp+优先队列)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/83513413

Ingress

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 102400/65535 K (Java/Others)
Total Submission(s): 474    Accepted Submission(s): 229


 

Problem Description

Brickgao, who profited from your accurate calculating last year, made a great deal of money by moving bricks. Now he became ``gay shy fool'' again and recently he bought an iphone and was deeply addicted into a cellphone game called Ingress. Now he is faced with a problem so he turns to you for help again. We make some slight modifications based on the original rules, so please draw attention to the details below.

There are N portals (indexed from 1 to N) around Brickgao's home, and he can get some substances called XM by hacking the portals. It's known that for each portal i, he can get Ai XM during the first hack, and after each hack, the amount of XM he will get during the next hack will decrease by Bi. If the amount of XM he can get is less than or equal to zero, Brickgao can't get XM from that portal anymore. For the i-th portal, if Ai=10,Bi=2 and he hacks 3 times, he will get 10, 8, 6 XM during each hack.

There are M bidirectional roads between some pairs of portals and between Brickgao's home and some portals. Now he is eager to start his Ingress journey from home and finally return home, but due to the extremely hot weather, Brickgao will feel sick when you hack more than K times or the distance he covers is more than L. So how much XM he can get at most during this journey?

 

 

Input

The first line contains a single integer T(T≤20), indicating the number of test cases.

The first line of each case are four integers N(1≤N≤16),M(0≤M≤N(N+1)2),K(1≤K≤50) and L(2≤L≤2000).
The second line of each case contains N non-negative integers where the i-th denotes Ai(Ai≤500).
The third line of each case contains N non-negative integers where the i-th denotes Bi(Bi≤50).
Each of next M line contains 3 non-negative integers u,v(0≤u,v≤n) and c(0≤c≤1000) , denotes that there is a road with the length of c between the u-th and the v-th portal. If u or v equals to 0, it means Brickgao's home.

 

Output

For each test case, output the case number first, then the amount of maximum XM Brickgao can get.

 

Sample Input

 

2

1 1 3 2

5

3

0 1 1

3 6 3 5

10 7 5

2 3 1

0 1 3

0 2 1

0 3 1

1 2 2

2 3 3

1 3 4

 

Sample Output

 

Case 1: 7

Case 2: 16

 题意:

给你一张图,起点是0,有n+1个点,每到一个点你可以选择hack这个点,那么你就可以获得a[i]的能量,

同时该点的能量减少b[i],变为a[i]-b[i],再被hack的话,你能得到的能量是a[i]-b[i],这个点的能量变为a[i]-2*b[i]

问你从0出发最后回到0你能获得最大的能量

解析:

其实这道题正解是状压dp+优先队列。

我感觉挺巧妙的,以下是队友的友情链接讲解

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+10;
const int INF  = 0x3f3f3f3f;
int a[20];
int p[20];
int dp[MAXN][20];
int dist[20][20];
int path[20][20];
int A[20][20];
int val[20],dec[20];
int n;

typedef struct node
{
    int val;
    int id;
    friend bool operator<(node a,node b)
    {
        return a.val<b.val;
    }
}node;

void init()
{

    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        A[i][j] = i==j?0:INF;//其实这里d[i][j]应该还要通过输入读数据的
    }
    //读取其他dist[i][j]的值
}

void floyd()
{
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        dist[i][j]=A[i][j];
        path[i][j]=((1<<(j-1))|(1<<(i-1)));
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(dist[i][k]<INF && dist[k][j]<INF && dist[i][k]>0 &&dist[k][j]>0)
                {
                    if(dist[i][j]>dist[i][k]+dist[k][j])
                    {
                        dist[i][j] = dist[i][k]+dist[k][j];
                        path[i][j] = (path[i][k]|path[k][j]);
                    }

                }
}

inline int getValue(int x,int u)
{
    if(x==1) return 0;
    return val[x]*u-(u-1)*u/2*dec[x];
}




int main()
{
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        cas++;
        int K,L;
        memset(dp,INF,sizeof(dp));

        int m;
        scanf("%d%d%d%d",&n,&m,&K,&L);
        n++;
        init();
        for(int i=2;i<=n;i++) scanf("%d",&val[i]);
        for(int i=2;i<=n;i++) scanf("%d",&dec[i]);
        for(int i=1;i<=m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            x++;
            y++;
            A[x][y]=min(A[x][y],w);
            A[y][x]=min(A[y][x],w);
        }
        floyd();
        dp[1][1]=0;

        for(int i=0;i<(1<<(n));i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(dp[i][j]==INF) continue;
                if((i&(1<<(j-1)))==0) continue;

                for(int k=1;k<=n;k++)
                {
                    dp[i|path[j][k]][k]=min(dp[i|path[j][k]][k],dp[i][j]+dist[j][k]);
                }
            }
        }
        int ans=0;
        for(int i=0;i<(1<<(n));i++)
        {
            if(dp[i][1]>L) continue;
            priority_queue<node> mq;
            for(int j=1;j<=n;j++)
            {
                if((i&(1<<(j-1)))==0) continue;
                mq.push(node{val[j],j});
            }
            int res=0;
            for(int j=1;j<=K;j++)
            {
                node tmp=mq.top();
                mq.pop();
                res+=tmp.val;
                tmp.val=max(0,tmp.val-dec[tmp.id]);
                mq.push(tmp);

            }
            ans=max(res,ans);

        }

        printf("Case %d: ",cas);
        printf("%d\n",ans);
    }
}

我自己是用两次状压dp来做的,后面那一次的dp理论上的最坏复杂度达到O((2^n)*K*K*n)

但是这样就过了.....我本来只想莽一发的,但是就过了

hack[i][k]表示当前状态下严格取k次的最大获得的能量。

推荐还是用正解来做这道题,因为贴这篇博客的目的就是记录一下正解的做法。

我自己的做法只是一个参考。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+10;
const int INF  = 0x3f3f3f3f;
int a[20];
int p[20];
int dp[MAXN][20];
int dist[20][20];
int path[20][20];
int A[20][20];
int val[20],dec[20];
int n;
int hack[MAXN][61];

void init()
{

    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        A[i][j] = i==j?0:INF;//其实这里d[i][j]应该还要通过输入读数据的
    }
    //读取其他dist[i][j]的值
}

void floyd()
{
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        dist[i][j]=A[i][j];
        path[i][j]=((1<<(j-1))|(1<<(i-1)));
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(dist[i][k]<INF && dist[k][j]<INF && dist[i][k]>0 &&dist[k][j]>0)
                {
                    if(dist[i][j]>dist[i][k]+dist[k][j])
                    {
                        dist[i][j] = dist[i][k]+dist[k][j];
                        path[i][j] = (path[i][k]|path[k][j]);
                    }

                }
}

inline int getValue(int x,int u)
{
    if(x==1) return 0;
    return val[x]*u-(u-1)*u/2*dec[x];
}




int main()
{
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        cas++;
        int K,L;
        memset(dp,INF,sizeof(dp));

        int m;
        scanf("%d%d%d%d",&n,&m,&K,&L);
        n++;
        init();
        for(int i=2;i<=n;i++) scanf("%d",&val[i]);
        for(int i=2;i<=n;i++) scanf("%d",&dec[i]);
        for(int i=1;i<=m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            x++;
            y++;
            A[x][y]=min(A[x][y],w);
            A[y][x]=min(A[y][x],w);
        }
        floyd();
        dp[1][1]=0;

        for(int i=0;i<(1<<(n));i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(dp[i][j]==INF) continue;
                if((i&(1<<(j-1)))==0) continue;

                for(int k=1;k<=n;k++)
                {
                    dp[i|path[j][k]][k]=min(dp[i|path[j][k]][k],dp[i][j]+dist[j][k]);
                }
            }
        }
        memset(hack,-1,sizeof(hack));
        hack[1][0]=0;
        int ans=0;
        for(int i=0;i<(1<<(n));i++)
        {
            if(dp[i][1]>L) continue;
            for(int u=0;u<=K;u++)
            {
                if(hack[i][u]==-1) continue;
                ans=max(ans,hack[i][u]);
                for(int j=1;j<=n;j++)
                {
                    if(i&(1<<(j-1))) continue;
                    for(int w=0;w<=K-u;w++)
                    {
                        hack[i|(1<<(j-1))][w+u]=max(hack[i|(1<<(j-1))][w+u],hack[i][u]+getValue(j,w));

                    }

                }
            }
        }

        printf("Case %d: ",cas);
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/83513413