POJ 1275 Cashier Employment
这是《算法艺术与信息学竞赛》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; }