這道題是昨天linkfqy dalao上課講的一道題
當時他講的時候就想到了一種玄學的搞法,然後不敢相信自己切掉了
沒想到後來CHJ dalao也想到了這種演算法,然後發現是對的
後來10min就切掉了
看來以後要活躍一點了
這裡主要講兩種演算法(想WQS二分這種高深的演算法就不講了)
首先講題意:一個人它只能在某個時間點去跟看k個他的客人對話,或是結束他和當前這個客人的對話。但是它不能在兩個連續的時間段進行講話,問它最少需要多少時間來和k個客人對話。
1.堆
我們先按時間排序,處理出每一個時間段的長度
然後就建模轉化成:n-1個點中取k個並且每兩個都不相鄰的最少代價和。
這就和一道題很像了,看Luogu P1484&sol
用堆水一水即可
CODE
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=500005;
int k,n,x[N],a[N],pre[N],nxt[N],ans;
bool vis[N];
struct data
{
int x,num;
bool operator <(const data &s) const
{
return s.x<x;
}
};
priority_queue<data> small;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(k); read(n);
for (i=1;i<=n;++i)
read(x[i]);
sort(x+1,x+n+1);
for (i=2;i<=n;++i)
{
a[i-1]=x[i]-x[i-1];
pre[i-1]=i-2; nxt[i-1]=i;
small.push((data){a[i-1],i-1});
}
a[0]=a[n]=1e9; pre[n]=n-1; nxt[0]=1;
while (k--)
{
while (vis[small.top().num]) small.pop();
data p=small.top(); small.pop(); ans+=p.x;
p.x=a[p.num]=a[pre[p.num]]+a[nxt[p.num]]-a[p.num];
vis[pre[p.num]]=vis[nxt[p.num]]=1;
pre[p.num]=pre[pre[p.num]]; nxt[pre[p.num]]=p.num;
nxt[p.num]=nxt[nxt[p.num]]; pre[nxt[p.num]]=p.num;
small.push(p);
}
printf("%d",ans);
return 0;
}
2.DP
首先這種題目就是看一眼就想到DP的吧
我們令f[i][j][0/1]表示前i個點中取j個並且第i個點取or不取(1表示取,2表示不取)的最少代價
則很容易寫出轉移:
f[i][j][1]=f[i-1][j-1][0]+a[i]
f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1])
首先我們發現i的那一維可以滾動儲存優化掉
然後空間就夠了,但是時間為O(nk)
然後堅信CF的機子是無敵的滿懷信仰地交上去說不定就過了
然後這裡我們就要講一個結論了:
無論怎麼取,都只可能取到3*k小的數
Why?很簡單,如果所有的數左右都取,算上它自己,也就3個,擴充套件開就是3*k
所以我們就把時間優化到了O(k^2)
CODE
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=500005,K=5005;
int x[N],a[N],n,k,f[2][K][2],pre,num,tot;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i,j;
read(k); read(n);
for (i=1;i<=n;++i)
read(x[i]); sort(x+1,x+n+1);
for (i=2;i<=n;++i)
x[i-1]=a[i-1]=x[i]-x[i-1];
sort(x+1,x+n); num=3*k<n?x[3*k]:x[n-1];
memset(f[0],63,sizeof(f[0])); f[0][0][0]=f[0][0][1]=0;
int now=0,lst=1,pre=-1;
for (i=1;i<n;++i)
{
if (a[i]>num) continue;
now^=1; lst^=1; memset(f[now],63,sizeof(f[now]));
++tot;
if (i-1!=pre)
{
for (j=1;j<=min(tot,k);++j)
{
f[now][0][0]=f[now][0][1]=0;
f[now][j][1]=min(f[lst][j-1][0],f[lst][j-1][1])+a[i];
f[now][j][0]=min(f[lst][j][0],f[lst][j][1]);
}
} else
{
for (j=1;j<=min(tot,k);++j)
{
f[now][0][0]=f[now][0][1]=0;
f[now][j][1]=f[lst][j-1][0]+a[i];
f[now][j][0]=min(f[lst][j][0],f[lst][j][1]);
}
} pre=i;
}
printf("%d",min(f[tot&1][k][0],f[tot&1][k][1]));
return 0;
}