HDU 5711 Ingress(狀壓dp+優先佇列)
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);
}
}