2018-04-26 模拟考试 答题报告
【留坑待填】...
一.任务分配
Description
图书馆按顺序排列有N本书需要维护,每本书的总页数不一定相同。现有M位员工。可以给每个员工分配连续的一段书籍,让他进行维护。现在的问题是,怎么样分配,工作任务最重(需要维护的页数最多)的人维护的页数尽量少。
Input
第一行两个数,N、M。接下来N行,每行一个整数,表示一本书的页数。
Output
任务最重的人最少需要维护的页数。
Sample Input 1
5 3 3 2 4 1 5
Sample Output 1
5
Hint
对于10%的数据,N<=10
对于30%的数据,N<=10^4
对于100%的数据,N<=10^5,M<=N。
一本书的页数最多10^4。
老师说这次全是dp, 我还真的信了555...;
这tm显然是二分啊, 做过了三次了不说了;
代码 :
//By zZhBr #include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define int long long int n, m; int a[100010]; int sum, lss; bool check(int x) { int non = 0; int lst = 0; for(register int i = 1 ; i <= n ; i ++) { lst += a[i]; if(lst > x) { non++; // printf("mid == %d, lst == %d, non == %d\n",x, lst, non); lst = 0; i = i - 1; // continue; } } if(lst != 0) non++; //printf("mid == %d, non == %d\n", x, non); if(non > m) return 1; else return 0; } signed main() { cin >> n >> m; for(register int i = 1 ; i <= n ; i ++) { scanf("%lld", &a[i]); sum += a[i]; lss = max(lss, a[i]); } if(m >= n) { int ans = 0; for(register int i = 1 ; i <= n ; i ++) { ans = max(ans, a[i]); } cout << ans << endl; return 0; } int l = lss, r = sum , mid; while(l < r) { int mid = l + r >> 1; // cout << mid << endl; bool f = check(mid); if(f) { l = mid + 1; } else { r = mid; } } cout << l << endl; return 0; }
水!
二.吃夜宵
Description
众所周知,pb是个长相好,身材好,人品好的三好少年,他每个月都要拿出很多的零花钱来孝敬父母、孝敬老师等等。所以这个月他的荷包终于顶不住了!这天晚上,当他和室友去吃夜宵时,看到那么多喷香的夜宵,他恨不得每样都吃一遍,但是他的经济情况已经不允许了。但是第二天就是发工资的日子,所以他决定今朝有酒今朝醉,把所有的钱都花掉! 一共有n样夜宵,每样都有一个价格Vi,pb每样夜宵最多买一样!他一共有C元钱,因为他想尽量多吃点夜宵,所以当他买完夜宵后,剩余的钱一定不够再买任何一样其他的夜宵了。他想知道一共有多少种买法,好来看看哪种买法最合算。
Input
第一行一个数T,表示一共T组数据。每组数据第一行两个数N,C。接下来一行N 个数,表示N种夜宵的价格。
Output
输出T行,每行第一个数为数据编号,然后是买法总数。
Sample Input 1
1 6 25 8 9 8 7 16 5
Sample Output 1
1 15
Hint
对10%的数据,N<=10, C<=200
对20%的数据,C<=1000
T<=10 , N <= 30 , C <= 10000
这题明显背包!
然而我没做出来。
可以看这个代码666: %%%YoungNeal
这个坑填不了;
算了留下我的辣鸡10分代码
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <bitset> using namespace std; #define LL long long int T, n, m; int a[35]; int main() { cin >> T; for(register int time = 1 ; time <= T ; time ++) { scanf("%d%d", &n, &m); for(register int i = 1 ; i <= n ; i ++) { scanf("%d", &a[i]); } sort(a + 1, a + 1 + n); // LL f[(1<<n)+1]; // memset(f, 0, sizeof f); LL e = 1 << n; LL cnt = 0; for(register LL i = 0 ; i <= e ; i ++) { LL my = 0; bool flag = 0; for(register int j = 1 ; j <= n ; j ++) { if(i & (1 << (j - 1))) { my += a[j]; } if(my > m) { goto End; } } for(register int j = 1 ; j <= n ; j ++) { if(i & (1 << (j - 1))) continue; if(my + a[j] <= m) goto End; else if(my + a[j] > m) break; } cnt++; // printf("status : %d, money : %d, cnt : %d\n", i, my, cnt); End:; } printf("%d %lld\n", time, cnt); continue; } return 0; }
经过一下午的挣扎加上te神犇的指导, 终于A了这道题;
下面说说思路;
先进行一波排序;
设dp[i] [j], 表示对于第i个数, 0 ~ i的数必须选, i+1这个数必须不选, i+2~n的数可选可不选;
我们为什么要这么设? 因为我们发现,要保证最后的结果不能买任何东西, 我们就要知道我们所没有选的最廉价的物品是什么;
因为我们从小到大排了序, 就保证了i+1是我们所没有选择的最小的数;(机智)!
这样定义的话, 我们就把这个问题分成了n+1个阶段, 每个阶段互不干扰, 便于最后求和, 棒棒的!
我们首先先求出排序后序列的前缀和, 然后进行第一波转移(其实是初始化) : dp[i] [m-sum[i]] = 1;(m - sum[i] >= 0);
如果a[1] > m 直接退出byby。
然后从0开始枚举i;为什么从0开始枚举? 因为保证枚举到所有的状态, 当i == 0的时候的状态也是独立的一个状态;
第二维枚举j : i + 2 ->n, 为什么要从i+2开始?因为我们保证了i+1不选, 而我们要从i+2到n中选择所有可以选择的数进行转移;
第三维枚举p值, 从0到m - a[i], 为什么不是从m-a[i]到0? 因为, 我们要保证每一个数只选择一次,如果从后往前转移的话, 我们就不能保证这个数只选择了一次;
枚举所有可能的情况求和;ok。
代码奉上:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define int long long int T, n, m; int a[10010]; int dp[35][10010]; int sum[10010]; signed main() { cin >> T; for(register int time = 1 ; time <= T ; time ++) { memset(dp, 0, sizeof dp); memset(sum, 0, sizeof sum); scanf("%lld%lld", &n, &m); for(register int i = 1 ; i <= n ; i ++) { scanf("%lld", &a[i]); } sort(a + 1, a + 1 + n); if(a[1] > m) { printf("%d 0\n", time); continue; } // a[n+1] = m; for(register int i = 1 ; i <= n ; i ++) { sum[i] = sum[i-1] + a[i]; } dp[0][m] = 1; for(register int i = 0 ; i <= n ; i ++) { if(m - sum[i] >= 0) { dp[i][m-sum[i]] = 1; //printf("dp[0][%d] == %d \n ",m-sum[i],dp[0][m-sum[i]]); } } for(register int i = 0 ; i <= n ; i ++) { for(register int j = i + 2 ; j <= n ; j ++) { for(register int p = 0 ; p + a[j] <= m ; p ++) { dp[i][p] += dp[i][p+a[j]]; // if(dp[i][p]!=0)printf("dp[%d][%d]=%d\n",i,p,dp[i][p]); } //cout << endl; } } int ans = 0; for(register int i = 0 ; i <= n ; i ++) { for(register int j = 0 ; j < a[i+1] ; j ++) { ans += dp[i][j]; } } printf("%lld %lld\n", time , ans); } return 0; }
三.货车运输
(待添加)...