week13作業
E-TT的神祕任務3
題目大意
給定一個環,,其中 的左邊是 。要求從環上找出一段長度不超過 的連續序列,使其和最大。
題解
其實遇到環的問題,多半一上來的任務就是拆環
sum[i]表示前綴和
那麼答案其實就是其中
單調隊列維護在m個數之內的最小值的下標,然後掃一遍即可出解。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 200003
using namespace std;
int n,k,a[N],sum[N],q[N];
int main()
{
int T; scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i+n]=a[i];
n=2*n;
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
int head=0,tail=0,ans=-1e9;
int l,r;
q[head]=0;
for(int i=1;i<=n;i++) {
while(head<=tail&&i-q[head]>k) head++;
int j=q[head];
if (sum[i]-sum[j]>ans) ans=sum[i]-sum[j],l=j+1,r=i;
while(head<=tail&&sum[i]<sum[q[tail]]) tail--;
q[++tail]=i;
}
if(l>n/2) l-=n/2;
if(r>n/2) r-=n/2;
printf("%d %d %d\n",ans,l,r);
}
return 0;
}
week14作業
E-Q老師想度假
題目大意
已知 Q老師 一共有 M 件襯衫,且如果昨天穿的是襯衫 A,今天穿的是襯衫 B,則 Q老師 今天可以獲得 快樂值。
在 N 天假期結束後,Q老師 最多可以獲得多少快樂值
題解
矩陣乘法優化DP
表示第i天,穿衣服爲j所獲得的快樂值總和
狀態轉移方程爲,直接求解時間複雜度,這個時間複雜度肯定是不行的,通過觀察可以發現狀態轉移方程非常的規整,M的取值比較小,且max操作是滿足結合律的,那麼我們可以用矩陣乘法來進行DP加速。
在做矩陣乘法時,我們只需要把變成即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
int N,n,m;
struct data
{
LL a[103][103];
}e;
void clear(data &x)
{
for (int i=1;i<=N;i++)
for (int j=1;j<=N;j++)
x.a[i][j]=0;
}
void change(data &a,data b)
{
for (int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
a.a[i][j]=b.a[i][j];
}
data mul(data a,data b)
{
data c;
for (int i=1;i<=N;i++)
for (int j=1;j<=N;j++)
{
c.a[i][j]=0;
for (int k=1;k<=N;k++)
c.a[i][j]=max(c.a[i][j],a.a[i][k]+b.a[k][j]);
}
return c;
}
data pow(data num,int x)
{
data ans; clear(ans);
//for (int i=1;i<=N;i++) ans.a[i][i]=1;
data base; change(base,num);
while(x)
{
if (x&1) ans=mul(ans,base);
x>>=1;
base=mul(base,base);
}
return ans;
}
int main()
{
while (scanf("%d%d",&n,&N)!=EOF) {
clear(e);
for (int i=1;i<=N;i++)
for (int j=1;j<=N;j++) scanf("%lld",&e.a[i][j]);
data ans=pow(e,n-1);
LL k=0;
for (int i=1;i<=N;i++)
for (int j=1;j<=N;j++) k=max(k,ans.a[i][j]);
printf("%lld\n",k);
}
return 0;
}
week14限時訓練
A-貓睡覺問題
題目大意
給出一些24小時中不相交的一些時間段,要求這些時間段貓是醒着的,貓連續醒着的時間不能超過B小時,一旦開始睡覺必須不少於A小時,求是否能滿足要求,若能則輸出睡覺的時間段
題解
模擬題
首先我們把時間表示變成分鐘,例如01:01=>61
然後我們對以時間段的左端點進行排序,那麼如果存在跨越24:00的時間段,我們只需要將該時間的右端點加上24小時即可
然後我們開始遍歷每個時間段,每遍歷到一個時間段,我們就判斷上個時間段結束到當前時間段開始這段時間是否大於等於A,若大於等於則清空連續醒着的時間,並把這段時間記入睡覺時間,否則這段時間記入連續醒着的時間。然後判斷連續醒着的時間加上當前時間段是否大於B,如果大於B,則說明沒有合法方案,若小於等於B,則當前時間段記入連續醒着的時間。
需要特別注意的是,最後一個時間段和第一個時間段,因爲他每一天都是一樣的,所以是一個循環往復的過程。如果最後一個時間段結束和第一個時間段開始之間的時間可以睡覺,那麼就睡覺,否則判斷從最後一個時間段開始到第一次睡覺開始這段時間是否小於等於B,如果大於B則不存在合法方案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int T=24*60;
struct data{
int s,t;
}a[30],b[30];
int A,B,n,m;
int change(int x,int y)
{
return x*60+y;
}
bool cmp(data a,data b)
{
return a.s<b.s;
}
int main()
{
while(scanf("%d%d%d",&A,&B,&n)!=EOF) {
m=0;
for (int i=1;i<=n;i++) {
int h1,m1,h2,m2;
scanf("%d:%d-%d:%d",&h1,&m1,&h2,&m2);
a[i].s=change(h1,m1);
a[i].t=change(h2,m2);
if (a[i].t<a[i].s) a[i].t+=T;
}
sort(a+1,a+n+1,cmp);
bool pd=true;
if (a[1].t-a[1].s+1>B*60) {
printf("No\n");
continue;
}
int s=a[1].s;
for (int i=2;i<=n;i++)
if (a[i].s-1-a[i-1].t>=A*60) {
++m;
b[m].s=a[i-1].t+1; b[m].t=a[i].s-1;
s=a[i].s;
}
else if(a[i].t-s+1>B*60) {
pd=false;
break;
}
if(a[1].s-1+T-a[n].t>=A*60) {
++m;
b[m].s=(a[n].t+1)%T;
b[m].t=(a[1].s-1+T)%T;
}
else if(!m||b[1].s-1+T-s+1>B*60) pd=false;
if(!pd) {
printf("No\n");
continue;
}
printf("Yes\n");
printf("%d\n",m);
for (int i=1;i<=m;i++) {
int h1,m1,h2,m2;
h1=b[i].s/60;
m1=b[i].s%60;
h2=b[i].t/60;
m2=b[i].t%60;
printf("%02d:%02d-%02d:%02d\n",h1,m1,h2,m2);
}
}
return 0;
}