题目大意:有一个学校要招老师,给你s门课,m个现役的老师,给你这m个老师分别教哪些课和他们的cost,现役的老师必须要继续留着,然后再给你n个应聘者,分别给你这n个人教哪些课和他们的cost,现在要求每门课至少要有两个老师教,问你最少的cost。
思路:基础的三进制的状态压缩DP吧,基本思想和二进制一样,只是这里需要多处理一下,用num[ i ][ j ] 表示数 i 如果用三进制表示,那么第 j 位为多少,设d[ s ] 表示状态s的最少花费,那么d[ s|si ] = min(d[ s ] +cost[ i ])。
在这里吐槽一下,这道题读入数据有点坑,没告诉你课的数目,我用的是先字符串读进去,再一个一个抠。。 = =
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int INF = 0x0fffffff ; int my_pow(int x) { int s = 1; for(int i =1;i<=x;i++) s *= 3; return s; } int num[22222][11]; void init() { memset(num,0,sizeof(num)); int s = my_pow(9); for(int i = 1;i<s;i++) { int t = i; for(int j = 0;t;j++) { num[i][j] = t%3; t/=3; } } } struct Emp { int c; int s; Emp(){ s = 0;} } emp[111]; int bin(int x,int y,int lim) { int tmp = 1; int s = 0; for(int i = 0;i<lim;i++) { if(num[x][i]+num[y][i]>=3) s += 2*tmp; else s += (num[x][i]+num[y][i])*tmp; tmp *= 3; } return s; } int d[22222] ; char str[111]; int main() { init(); int s,m,n; while(~scanf("%d%d%d",&s,&m,&n)&&s) { getchar(); int S = my_pow(s)-1; for(int i = 0;i<=S;i++) d[i] = INF; int S0 = 0; int sum = 0; for(int i = 1;i<=m;i++) { gets(str); for(int j = 0;str[j]!='\0';) { if(str[j]>='0'&&str[j]<='9') { int tmp = str[j]-'0'; j++; while(str[j]>='0'&&str[j]<='9') { tmp = tmp*10 + str[j]-'0'; j++; } if(tmp>=100) { sum += tmp; } else { if(num[S0][tmp-1]<2) S0 += my_pow(tmp-1); } } else j++; } } d[S0] = sum; //printf("S0 = %d,sum = %d\n",S0,sum); for(int i = 1;i<=n;i++) { emp[i].s = 0; gets(str); for(int j = 0;str[j]!='\0';) { if(str[j]>='0'&&str[j]<='9') { int tmp = str[j]-'0'; j++; while(str[j]>='0'&&str[j]<='9') { tmp = tmp*10 + str[j]-'0'; j++; } if(tmp>=100) { emp[i].c = tmp; } else { emp[i].s += my_pow(tmp-1); } } else j++; } } for(int i = 0;i<n;i++) { for(int j = S;j>=0;j--) { int to = bin(j,emp[i+1].s,s); d[to] = min(d[to],d[j]+emp[i+1].c); //printf("i+1 =%d,d[%d] = %d\n",i+1,to,d[to]); } } printf("%d\n",d[S]); } return 0; } /* 2 2 2 10000 1 20000 1 30000 1 2 40000 1 2 */