hdu 4281
地址:http://acm.hdu.edu.cn/showproblem.php?pid=4281
题意:有n个任务等待完成,每个任务有位置,所需时间,每个人最多m分钟。
第一问是求这些任务最少几个人完成;第二问是给无数个人,问最少需要多少分钟完成所有任务,并且每个人上限还是m。
mark:第一问可以状态dp,可以01背包记录一下,然后回溯。
第二问参见了大牛的做法,先状态dp,然后暴力搜索一下。
今天才了解到状态压缩dp可以有两种方式写,第一种是从前往后扫一遍,每次转换这个状态能够到达的状态(因为状态肯定是由值小的到值大的)
第二种是用队列来写,能够到达的状态都进队。类似与bfs。
代码:
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <queue> #define s(x) ((x)*(x)) using namespace std; const int maxn = (1 << 16); const int INF = 10000000; int d[20][20], sum[maxn]; int n,m; int x[20],y[20],c[20]; int min(int a, int b) {return a < b ? a : b;} void solve1() { int dp1[maxn], dp2[maxn]; int i,j,k; memset(dp1, -1, sizeof(dp1)); dp1[0] = dp2[0] = 0; for(i = 0; i < (1 << n); i++) { if(dp1[i] < 0) continue; for(j = 0; j < n; j++) { if(i&(1<<j)) continue; int ss = (i|(1<<j)); int d1,d2; if(dp2[i] >= c[j]) d1 = dp1[i], d2 = dp2[i]-c[j]; else d1 = dp1[i]+1, d2 = m-c[j]; if(dp1[ss] < 0 || dp1[ss] > d1 || dp1[ss] == d1 && dp2[ss] < d2) dp1[ss] = d1, dp2[ss] = d2; } } printf("%d ", dp1[i-1]); } int dp[maxn][20], vis[maxn][20]; int ans[maxn]; void solve2() { int i,j,k; queue<int> q; for(i = 0; i < n; i++) for(j = 0; j < n; j++) { if(i == j) d[i][j] = 0; else d[i][j] = ceil(sqrt(s(x[i]-x[j])+s(y[i]-y[j]))); } for(i = 0; i < (1 << n); i++) { sum[i] = 0; for(j = 0; j < n; j++) if(i&(1<<j)) sum[i] += c[j]; } memset(dp, -1, sizeof(dp)); dp[0][0] = 0; vis[0][0] = 1; q.push(0); while(!q.empty()) { int qq = q.front(); q.pop(); int s1 = qq%70000, s2; qq /= 70000; vis[s1][qq] = 0; for(i = 0; i < n; i++) { if(s1&(1<<i)) continue; s2 = s1|(1<<i); if(sum[s2] > m) continue; if(dp[s2][i] < 0 || dp[s2][i] > dp[s1][qq]+d[i][qq]) { dp[s2][i] = dp[s1][qq]+d[i][qq]; if(!vis[s2][i]) { vis[s2][i] = 1; q.push(i*70000+s2); } } } } memset(ans, -1, sizeof(ans)); for(i = 0; i < (1 << n); i++) for(j = 0; j < n; j++) { if(i&(1<<j) && dp[i][j] > 0) if(ans[i] < 0 || ans[i] > dp[i][j]+d[0][j]) ans[i] = dp[i][j]+d[0][j]; } k = (1 << n)-2; for(i = 1; i < (1 << n); i++, k--) { if(ans[i] < 0) continue; for(j = k; j; j = (j-1)&k) { if(ans[j] < 0) continue; if(ans[i+j] < 0 || ans[i+j] > ans[i]+ans[j]) ans[i+j] = ans[i]+ans[j]; } } printf("%d\n", ans[(1 << n)-1]); } int main() { int i; while(~scanf("%d%d", &n, &m)) { for(i = 0; i < n; i++) scanf("%d%d", x+i, y+i); for(i = 0; i < n; i++) scanf("%d", c+i); for(i = 0; i < n; i++) if(c[i] > m) { printf("-1 -1\n"); break; } if(i < n) continue; solve1(); solve2(); } return 0; }