Description

【NOIP2014模拟10.25A组】钻石交易_NOIP

Solution

这题明显要用状压。
设f[i][j]表示走到第i个点,然后卖掉的钻石的二进制状态为j。
直接暴力用dfs和bfs来做可以对60分,bfs的用spfa来做。
那么f[i][j]=max(f[i][j],f[k][j|er[p-1]]-chang[i]+a[j][p])这是自己走别的点的情况,自己走自己的情况也类似。
但是把二进制放进去的冗余情况较多,因为一个二进制情况可以转化成很多个二进制情况。
我们考虑一下有没有二进制转移只转移一位的情况。
我们考虑枚举二进制状态z,然后求出所有点只卖这些钻石,的最长路。
设d[i]为当前状态z是的最短路(就是不买钻石然后更新别的值)。
然后用d值把f[i][z]的值向后多推一位。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=1507;
int i,j,k,l,t,n,m,ans,s,x,y,z,p,q;
int a[maxn][maxn];
int first[4007],next[4007],last[4007],num,chang[4007],da;
int f[1507][1507],er[11],cnt,b[1507][11];
int data[maxn*200],d[maxn];
bool bz[maxn],az;
void add(int x,int y,int z){
last[++num]=y,next[num]=first[x],first[x]=num;chang[num]=z;
}
void spfa(int zhi){
int i,head=0,tail=0,now,j,x;
data[1]=s;bz[s]=1;fo(i,1,n)d[i]=f[i][zhi],data[++tail]=i;
while(head<tail){
x=data[++head];
rep(i,x){
if(d[x]-chang[i]>=d[last[i]]){
d[last[i]]=d[x]-chang[i];
if(!bz[last[i]]){
bz[last[i]]=1;
data[++tail]=last[i];
}
}
}
bz[x]=0;
}
fo(i,1,m){
if(zhi&er[i-1])continue;
fo(j,1,n)if(d[j]>=0)f[j][zhi|er[i-1]]=max(f[j][zhi|er[i-1]],d[j]+a[j][i]);
}
}
int main(){
freopen("fan.in","r",stdin);
scanf("%d%d%d%d%d",&n,&t,&m,&k,&s);
er[0]=1;fo(i,1,10)er[i]=er[i-1]*2;
fo(i,1,n)fo(j,1,m)scanf("%d",&a[i][j]);
fo(i,1,t)scanf("%d%d%d",&x,&y,&z),add(x,y,z);
memset(f,128,sizeof(f));
f[s][0]=k;
fo(j,0,er[m]-1)spfa(j);
fo(i,1,n)fo(j,0,er[m]-1)ans=max(ans,f[i][j]);
printf("%d\n",ans);
// printf("%d\n",cnt);
}