这是《算法艺术与信息学竞赛》306页的原题,只不过是英文版。
在黑书上用的是最长路的算法 ,实际上最短路和最长路差不多的。
以下是黑书上的描述:
枚举sum,通过求最短路或最长路来求解,这是一种方法。
黑书中说的二分法是针对当s[-1]!=sum时,它与sum的关系来二分搜索的,这里就用代码描述了。
下面是我的代码,用的是最短路求解。黑书中的r数组和t数组代表意义一样,s数组代码中用的是dis数组。
以下是代码:
#include <stdio.h>
#include <queue>
using namespace std;
#define inf 0x7fffffff
struct node
{
int to,w,next;
} edge[25*30];
int r[25],t[25],dis[25],head[25],cnt,c[25];
bool vis[25];
void init()
{
cnt=0;
for(int i=0; i<25; i++)
{
head[i]=-1;
dis[i]=inf;
vis[i]=false;
c[i]=0;
}
dis[0]=0;
}
void add(int u,int v,int w)
{
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
cnt++;
}
void build(int ans)
{
init();
add(0,24,-ans);
for(int i = 1; i <= 24; ++i)
{
add(i - 1,i,0);
add(i,i - 1,t[i]);
}
for(int i = 17; i <= 24; ++i)
{
add(i ,(i+8)%24,-r[(i+8)%24] + ans);
}
for(int i = 1; i <= 16; ++i)
{
add(i,i+8,-r[i+8]);
}
}
bool spfa(int ans)
{
queue <int>q;
q.push(0);
vis[0]=true;
c[0]=1;
while(!q.empty())
{
int p,t=q.front();
q.pop();
p=head[t];
vis[t]=false;
while(p!=-1)
{
if(dis[edge[p].to]>dis[t]+edge[p].w)
{
dis[edge[p].to]=dis[t]+edge[p].w;
if(!vis[edge[p].to])
{
vis[edge[p].to]=true;
q.push(edge[p].to);
c[edge[p].to]++;
if(c[edge[p].to] > 24)
{
return false;
}
}
}
p=edge[p].next;
}
}
if(dis[24]==-ans)
{
return true;
}
else
{
return false;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int i,j,n,a;
bool flat=true;
for(i=1; i<=24; i++)
{
scanf("%d",&r[i]);
t[i]=0;
}
scanf("%d",&n);
for(i=1; i<=n; i++)
{
scanf("%d",&a);
t[a+1]++;
}
for(i=0; i<=n; i++)
{
build(i);
if(spfa(i))
{
printf("%d\n",i);
flat=false;
break;
}
}
if(flat)
{
printf("No Solution\n");
}
}
return 0;
}