题意:修课,问最少几个学期修完,有些课要修完别的才能学,一些课只能秋天学、春天学,或两学期都能学
思路:这类先后关系的题,应属于拓扑排序,这有点复杂,不好搞。于是用状压DP,dp[i][s],第i学期已经修的,去推第i+1学期的状态,dp[i][s] = 1,代表第i天能到达这个状态。求dp[i][en] = 1,最小的i。对于下学期能修的课(最多m门),暴力深搜下,数据不是很大。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<stdlib.h>
#include<math.h>
#include<vector>
#include<list>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
#include<numeric>
#include<functional>
using namespace std;
typedef long long ll;
const int maxn = (1<<12)+10;
int dp[1000][maxn],n,m,q[20],xu[20],s;
map<string,int> ma;
vector<int> v[20],vis;
void init()
{
for(int i = 0; i <= n; i++)
v[i].clear();
ma.clear();
memset(dp,0,sizeof dp);
}
void xue(int x)
{
int t = 1;
vis.clear();
while(t <= n)
{
if((x&1) == 0)
vis.push_back(t);
t++;
x >>= 1;
}
}
int ok(int x,int time)
{
if(q[x] != 2 && q[x] != time)
return 0;
for(int i = 0; i < v[x].size(); i++)
{
if(find(vis.begin(),vis.end(),v[x][i]) != vis.end())
return 0;
}
return 1;
}
void dfs(int x,int cnt,int time) // 0 F,1 S
{
if(cnt == m || x == vis.size())
{
int tp = s;
for(int i = 0; i < cnt; i++)
tp = tp | (1<<(xu[i]-1));
dp[time+1][tp] = 1;
return;
}
for(int i = x; i < vis.size(); i++)
{
dfs(i+1,cnt,time);
if(ok(vis[i],time%2))
{
xu[cnt] = vis[i];
dfs(i+1,cnt+1,time);
}
}
}
int main(void)
{
int i,j,k;
string sh;
while(scanf("%d%d",&n,&m)!=EOF && (n!=-1 || m!=-1))
{
init();
for(i = 1; i <= n; i++)
{
cin>>sh;
ma[sh] = i;
}
for(i = 1; i <= n; i++)
{
cin>>sh;
k = ma[sh];
cin>>sh;
if(sh[0] == 'F')
q[k] = 0;
else if(sh[0] == 'S')
q[k] = 1;
else
q[k] = 2;
scanf("%d",&j);
while(j--)
{
cin>>sh;
v[k].push_back(ma[sh]);
}
}
int en = (1<<n)-1;
dp[0][0] = 1;
i = 0;
while(1)
{
for(s = 0; s < en; s++)
{
if(dp[i][s] == 0)
continue;
dp[i+1][s] = 1;
xue(s);
dfs(0,0,i);
}
i++;
if(dp[i][en])
break;
}
printf("The minimum number of semesters required to graduate is %d.\n",i);
}
return 0;
}