1. 程式人生 > >CF 946D. Timetable 暴力+DP

CF 946D. Timetable 暴力+DP

題意:某星球一週有n天,每天m個小時.每天小A會在該天第一次上課時間p,直到最後一次上課時間q待在學校 共q-p+1小時.
給出課表s,若s[i][j]=='1'代表有上課 否則代表沒課.
問溜掉不超過k節課 使得小A這n天在學校的總時間最小. n,m,k<=500


如果每次只考慮刪一次最多能減小多少,容易找到反例
1 24 2
110000000000000101010101
跑出的結果為21 答案為9.


設d[i][j] 表示前i天翹j次課的最小在校時間 f[i][j]為第i天翹j節課時 該天在校的最短時間.
轉移 d[i][j]=min(d[i-1][p] + f[i][j-p])

預處理f[i][j],暴力列舉第i天最後剩下的在校線段[l,r],字首和求出刪除的個數即可.O(n^3).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e2+5,inf=0x3f3f3f3f;
int f[N][N],d[N][N];
int n,m,k,p[N];
char s[N][N];
int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
	cin>>n>>m>>k;
	memset(d,inf,sizeof(d));
	memset(f,inf,sizeof(f));
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s[i]+1);
		p[0]=0;
		for(int j=1;j<=m;j++)
			p[j]=p[j-1]+(s[i][j]=='1');
		f[i][p[m]]=0;
		for(int l=1;l<=m;l++)
		{
			for(int r=l;r<=m;r++)
			{
				int tot=p[l-1]+(p[m]-p[r]);
				if(p[r]-p[l-1])
					f[i][tot]=min(f[i][tot],r-l+1);
				else
					f[i][tot]=0;
			}
		}
		
	//	for(int j=0;j<=m;j++)
	//		cout<<i<<' '<<j<<' '<<f[i][j]<<'\n';	
	}
	for(int j=0;j<=k;j++)
		d[0][j]=0;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=k;j++)
			for(int p=0;p<=j;p++)
				d[i][j]=min(d[i][j],d[i-1][p]+f[i][j-p]);
	cout<<d[n][k]<<'\n';
	return 0;
}