946D Timetable(分組揹包)
Ivan is a student at Berland State University (BSU). There are n days in Berland week, and each of these days Ivan might have some classes at the university.
There are m working hours during each Berland day, and each lesson at the university lasts exactly one hour. If at some day Ivan's first lesson is during i
Ivan doesn't like to spend a lot of time in the university, so he has decided to skip some lessons. He cannot skip more than k
Given n, m, k and Ivan's timetable, can you determine the minimum number of hours he has to spend in the university during one week, if he cannot skip more than k lessons?
InputThe first line contains three integers n, m and k (1 ≤ n, m ≤ 500, 0 ≤ k ≤ 500) — the number of days in the Berland week, the number of working hours during each day, and the number of lessons Ivan can skip, respectively.
Then n lines follow, i-th line containing a binary string of m characters. If j-th character in i-th line is 1, then Ivan has a lesson on i-th day during j-th hour (if it is 0, there is no such lesson).
OutputPrint the minimum number of hours Ivan has to spend in the university during the week if he skips not more than k lessons.
ExamplesinputCopy2 5 1 01001 10110output
5inputCopy
2 5 0 01001 10110output
8Note
In the first example Ivan can skip any of two lessons during the first day, so he spends 1 hour during the first day and 4 hours during the second day.
In the second example Ivan can't skip any lessons, so he spends 4 hours every day.
題意:給你N天,每天M個小時的時間表,和一共能翹K節課,每節課佔一個小時,問你你在學校最少要留幾個小時?每天的小時數=最後一節課-第一節課的時間
解題思路:分組揹包。先是預處理出V[i][k]即第i天逃k節課的能節約的最多時間。把每天看成每一組,每一組內的物品就是逃0,1,2,3,。。。節課能節約的時間。然後容量就是K,每個物品重量為1,價值是V[i][k].
關於分組揹包:https://blog.csdn.net/kyriesnow/article/details/44256729
分組揹包,由基礎揹包演化而來的一種情況。
具體問題是這樣的。具體問題是這樣的。一個容量為V的揹包,還有若干組物品,每組包含若干物品,這些物品各不相同,而且體積w和價值p各不相同。組內的物品相沖突。求出能在不超過V的情況下儘可能的使價值最大。
乍一看好像很難的樣子,其實仔細想想很簡單,這種問題完全可以用01揹包解決。 對於分組揹包,可以這樣想:雖然分成很多組,但只能選一個,或者不選,這和01揹包是一樣的,也就是說,對於01揹包裡每一個獨一無二的物品,對應的分組揹包就是每一組中選擇一個物品,這樣來看,完全就是01揹包問題。
下面是狀態方程:
for(int i = 1; i <= z; i++)
for(int j = V; j >= 1; j--)
for(int k = 1; k <= n; k++)
dp[j] = max(dp[j - w[i][k]] + p[i][k], dp[j]);
其中,z是分組數,V是揹包體積,n是每組物品數量,w和p分別是第i組第k個物品的體積和價值。
為什麼要這樣寫呢?可以想一下01揹包的狀態方程,和這個外兩層的迴圈是一樣的,不一樣的是裡面又加了一層,這層迴圈是遍歷每一組的物品用的,對於dp[]中每一個狀態都是迴圈了一遍每一組的物品才換到下一個的,所以對後面的沒有影響,也就保證了每組物品最多隻有一件。
#include <bits/stdc++.h>
using namespace std;
int V[505][505];//第i天逃j節最多能省多少時間
int N,M,K;
string rc[505];
int num[505];//每天1的個數
int pos[505][505];//第i天,第j個1在哪個位置(用於快速就時間)
int dp[505];//分組揹包
int main()
{
scanf("%d%d%d",&N,&M,&K);
for(int i=0;i<N;i++){
cin>>rc[i];
for(int j=0;j<rc[i].length();j++){
if(rc[i][j]=='1'){
num[i]++;
pos[i][num[i]]=j;
}
}
}
//計算每一天
for(int i=0;i<N;i++){
int cnt=0;
//列舉長度,即翹的課數,注意是num[i]-1,不然的話會出現負數,num[i]特殊處理即可
for(int k=0;k<=min(num[i]-1,K);k++){
int res=0x3f3f3f3f;//每天要上課的最小小時數
//列舉右邊界
for(int p=0;p<=k;p++){
int q=(k-p);
res=min(res,pos[i][num[i]-q]-pos[i][p+1]+1);
}
V[i][k]=M-res;//節省了M-res個小時
}
V[i][num[i]]=M;//特殊處理,全翹了肯定是節省了M個小時
}
//分組揹包
memset(dp,0,sizeof(dp));
for(int i=0;i<N;i++){//列舉所有分組
for(int j=K;j>=0;j--){//列舉容量,這裡容量就是K
for(int k=0;k<=min(j,M);k++)//列舉組內物品數
dp[j]=max(dp[j],dp[j-k]+V[i][k]);
}
}
printf("%d\n",N*M-dp[K]);
return 0;
}