此題的核心就是選擇,按時間順序進行選擇的話,可以發現每次選擇只和當前選擇的集合中時間最晚的電話有關。
所以可以dp。
令f[i][j]為處理了前i個電話,當前選擇的電話中最晚的電話為j時,選擇的最少電話數。
對於i而言,如果選擇i,則有f[i][i]=min{f[i-1][j]},其中1<=j<i,且由j可以判斷出i的正確時間。
同時,如果i是今年的電話,則除了上面的轉移,還有f[i][i]=min(f[i][i],f[i-1][0]+1)。
如果不選擇i,則有f[i][j]=f[i-1][j]。
考慮到可能有前面沒有選擇的電話,所以這種情況用0表示。
注意邊界,如果i是必須選擇的,則f[i][0]=INF,否則f[i][0]=f[i-1][0]。
還要注意,最後一個電話才是最近的電話!!!
【代碼】
[cpp]
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=1003,INF=100000;
struct date
{
int mon,day,h,min;
bool operator >=(const date& y)
{
if (mon>y.mon) return true;
if (mon<y.mon) return false;
if (day>y.day) return true;
if (day<y.day) return false;
if (h>y.h) return true;
if (h<y.h) return false;
if (min>=y.min) return true;
else return false;
}
}a[N];
int f[N][N],y[N];
bool v[N];
int main()
{
int i,j,n,ty,ans;
char ch;
freopen("in","r",stdin);
while (1)
{
scanf("%d\n",&n);
if (n==0) break;
if (n<=10)
{
i=0;
}
for (i=n;i>=1;i--)
{
scanf("%d:%d:%d:%d %d %c\n",&a[i].mon,&a[i].day,&a[i].h,
&a[i].min,&j,&ch);
if (ch=='+') v[i]=true;
else v[i]=false;
}
y[1]=0;
for (i=2;i<=n;i++)
if (a[i]>=a[i-1]) y[i]=y[i-1]+1;
else y[i]=y[i-1];
f[1][1]=1;
if (v[1]) f[1][0]=INF;
else f[1][0]=0;
for (i=2;i<=n;i++)
f[1][i]=INF;
for (i=2;i<=n;i++)
{
for (j=1;j<=n;j++)
f[i][j]=INF;
if (v[i]) f[i][0]=INF;
else f[i][0]=f[i-1][0];
if (v[i])
{
if (y[i]==0) f[i][i]=min(f[i][i],f[i-1][0]+1);
for (j=1;j<i;j++)
if (f[i-1][j]<INF)
{
if (a[i]>=a[j]) ty=y[j]+1;
else ty=y[j];
if (ty!=y[i]) continue;
f[i][i]=min(f[i][i],f[i-1][j]+1);
}
}
else
{
for (j=1;j<=n;j++)
f[i][j]=f[i-1][j];
if (y[i]==0) f[i][i]=min(f[i][i],f[i-1][0]+1);
for (j=1;j<i;j++)
if (f[i-1][j]<INF)
{
if (a[i]>=a[j]) ty=y[j]+1;
else ty=y[j];
if (ty!=y[i]) continue;
f[i][i]=min(f[i][i],f[i-1][j]+1);
}
}
}
ans=INF;
for (i=1;i<=n;i++)
ans=min(ans,f[n][i]);
if (ans>=INF) ans=n;
printf("%d\n",ans);
}
}