题目链接:可以直接去Vj开题目 题目编号Aizu 1378-1388
A题
题意:给你一个容器,只能填方黑色和白色巧克力,黑色有1和k 2种规格,白色为1,问你在不超过l的情况下而且保证地段时黑色的情况下的方案数。
思想:DP DP[i][j] i=0为黑 1为白 i=0时候dp[i][j]=dp[1][j-1] i=1的时候 dp[i][j]=dp[i-1][j-1] + dp[i-1][j-k]//当j>=k的时候
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[2][105];//0黑1白
int main()
{
int l,k;
while(scanf("%d%d",&l,&k)!=EOF)
{
memset(dp,0,sizeof(dp));
dp[0][1]=1ll;
dp[0][k]=1ll;
for(int i=2;i<=l;i++)
{
dp[0][i]+=dp[1][i-1];
dp[1][i]+=dp[0][i-1];
if(i>=k)
dp[1][i]+=dp[0][i-k];
}
ll ans=0;
for(int i=1;i<=l;i++)
ans+=dp[0][i];
printf("%lld\n",ans);
}
return 0;
}
B题
题意:给你n个点,问你最多可以有多少平行线,2个点只能连接一条线,一个点只能在一个线内。
思想:本来直接贪心斜率相等然后在算上垂直的,然后WA。想了想可能会出现很多斜率不等的,但是都可以凑成一对一对的,这样就只能爆搜了,直接dfs搜索。剪枝即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct node{
int x;
int y;
}no[20];
int ans,n;
int k1[20];
int k2[20];
int vis[20];
void dfs(int id,int sum)
{
if(sum>=n/2)
{
int Ans=0;
for(int i=0;i<sum;i++)
for(int j=i+1;j<sum;j++)
if(k1[i]*k2[j]==k2[i]*k1[j])
Ans++;
ans=max(ans,Ans);
return ;
}
if(vis[id])//当前被搞了
dfs(id+1,sum);
else
{
for(int i=0;i<n;i++)
{
if(vis[i] || id==i)
continue;
k1[sum]=(no[i].y-no[id].y);
k2[sum]=(no[i].x-no[id].x);
vis[i]=1;
vis[id]=1;
dfs(id+1,sum+1);
vis[i]=0;
vis[id]=0;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d%d",&no[i].x,&no[i].y);
dfs(0,0);
printf("%d\n",ans);
}
C题
题意:给你n个人和一个t,每个人坚持每项的时间固定的a[i],问你t的时候n个人在检查或者等待检查某个项目。
思想:考虑下对于某个人来说他的最坏时间肯定是前边那个花费最多时间的那个人检查的项目,可以画画那个逻辑关系。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll a[maxn];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
ll ans=0;
ll temp = -1;
for(int i=0;i<n;i++)
{
ans+=a[i];
temp=max(temp,a[i]);
if(m>=ans)
printf("%lld\n",(m-ans)/temp+2);
else
printf("1\n");
}
}
return 0;
}
F题
题意:给定一个有重边的带正权有向图,保证一开始有从 1 到 2 的路径。对于每条边,询问把它反向之后,从 1 到 2 的最短路如何变化。
思想:考虑翻转某条边对结果是否有影响,如果当前边是DAG图上的桥的话,肯定会让结果变差,如果不是桥的话,肯定是不变或者变小。重点是判断当前边是否是DAG图上的桥,就需要tarjan缩点判断是否是桥了。
当结点u的子结点v的后代通过反向边只能连回v,那么删除这条边(u, v)就可以使得图G非连通了。用Tarjan算法里面的时间戳表示这个条件,就是low[v]>dfn[u]。
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
const int INF = 0x3f3f3f3f;
typedef long long ll;
struct node{
int to;
int valu;
int next;
int flag;
}no[maxn],No[maxn*2];//正向 反向
int head[maxn],Head[maxn];
int cnt,Cnt;
ll dist[maxn];//正向
ll Dist[maxn];//反向
void add(int u,int v,int valu,int flag)
{
no[cnt].to=v;
no[cnt].valu=valu;
no[cnt].flag=flag;
no[cnt].next=head[u];
head[u]=cnt++;
}
void Add(int u,int v,int valu,int flag)
{
No[Cnt].to=v;
No[Cnt].valu=valu;
No[Cnt].flag=flag;
No[Cnt].next=Head[u];
Head[u]=Cnt++;
}
void spfa(int u)
{
memset(dist,INF,sizeof(dist));
int vis[maxn]={0};
queue<int>q;
q.push(u);
vis[u]=1;
dist[u]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=no[i].next)
{
int v=no[i].to;
if(dist[v]>dist[u]+no[i].valu)
{
dist[v]=dist[u]+no[i].valu;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
}
void Spfa(int u)
{
memset(Dist,INF,sizeof(Dist));
int vis[maxn]={0};
queue<int>q;
q.push(u);
vis[u]=1;
Dist[u]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=Head[u];i!=-1;i=No[i].next)
{
int v=No[i].to;
if(Dist[v]>Dist[u]+No[i].valu)
{
Dist[v]=Dist[u]+No[i].valu;
if(vis[v]==0)
{
vis[v]=1;
q.push(v);
}
}
}
}
}
int dfn[maxn];
int low[maxn];
int is_bride[maxn];
int clo=1;
void tarjan(int u,int pre)
{
dfn[u]=low[u]=clo++;
for(int i=Head[u];i!=-1;i=No[i].next)
{
int v=No[i].to;
if(v!=pre)
{
if(dfn[v]==0)
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v] > dfn[u])
is_bride[No[i].flag]=1;
}
else
low[u]=min(low[u],low[v]);
}
}
}
int main()
{
cnt=0;
Cnt=0;
memset(head,-1,sizeof(head));
memset(Head,-1,sizeof(Head));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,valu;
scanf("%d%d%d",&u,&v,&valu);
add(u,v,valu,i);
Add(v,u,valu,i);
}
spfa(1);
Spfa(2);
memset(Head,-1,sizeof(Head));
Cnt=0;
ll Max=dist[2];
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=no[j].next)
{
int v=no[j].to;
if(dist[i]+Dist[v]+no[j].valu==Max)
{
Add(i,v,1,no[j].flag);
Add(v,i,1,no[j].flag);
}
}
}
tarjan(1,-1);
int ans[maxn]={0};
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=no[j].next)
{
if(dist[no[j].to]+Dist[i]+no[j].valu<Max)
ans[no[j].flag]=1;
else if(is_bride[no[j].flag])
ans[no[j].flag]=-1;
}
}
for(int i=1;i<=m;i++)
{
if(ans[i]==1)
printf("HAPPY\n");
else if(ans[i]==-1)
printf("SAD\n");
else
printf("SOSO\n");
}
return 0;
}
I题
题意:有n个人乘车,有两种不同的预定票的方式,问你他们分别需要预留出来的最少的座位数量。但是保证所有人都坐下
①方法是允许客户随便 预定,就是想要那个就给你那个。②是最优的那种分配。
思想:对于第二种简单,需要最多的肯定是同时上车人数最多的时候。第一种的话,肯定是对于这n个人中某个人预定的区间的人数的最大值。
两个vis大小不一 。。
#include<bits/stdc++.h>
using namespace std;
int a[200005];
int b[200005];
int vis[100005];//标记上车
int Vis[100005];//标记下车
int R[100005];//标记站点人
int main()
{
int n;
scanf("%d",&n);
int Max=-1;
for(int i=0;i<n;i++)
{
scanf("%d%d",&a[i],&b[i]);
vis[a[i]]++;//a[i]上车的人数
Vis[b[i]]++; //b[i]下车的人数
R[a[i]]++;//记录总人数
R[b[i]]--;
Max=max(Max,b[i]);
}
long long ans1=0;
long long ans2=0;
long long temp=0;
for(int i=1;i<=Max;i++)
{
temp+=R[i];
ans2=max(temp,ans2);
vis[i]+=vis[i-1];
Vis[i]+=Vis[i-1];
}
for(int i=0;i<=n;i++)
ans1=max(ans1,(long long)vis[b[i]-1]-Vis[a[i]]);//考虑的是区间最大值
printf("%lld %lld\n",ans1,ans2);
return 0;
}