Yougth的最大化
前面讲过,二分答案。
非常可乐
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升(正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S>=N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。
暴力bfs,用一个数组表示每个三个杯子中每个杯子的水的体积,然后开两重循环,模拟到水的过程,分能不能倒满两种情况讨论.主要是条件判断,意思要看懂,所谓喝一样多,不是边倒边喝,是要倒到一种这样的状态:三个容器中要有出现一种被平分的状态:比如 s=6,n=4,m=1; 可以达到s=3,n=3,m=0的状态; 又或是s=10,n=3,m=2; 可以达到 s=5,n=3,m=2的状态 ,这一点我是临时修改的,我觉得,这样也算是平分了,反正两位童鞋可以通过这种方法喝到一样多。但是 s=6,n=1,m=1,这种就不行,因为不能边喝边倒
#include<bits/stdc++.h>
using namespace std;
int b[3],vis[101][101][101],half;
struct node
{
int cur[3],s; //存每个杯子的水的体积
}p,temp;
void bfs()
{
queue<node>q;
p.cur[0]=b[0];p.cur[1]=p.cur[2]=p.s=0;
q.push(p);
while(!q.empty())
{
p=q.front();q.pop();
for(int i=0;i<3;i++) //模拟倒水
{
if(p.cur[i]>0)
{
for(int j=0;j<3;j++)
{
temp=p;
if(i!=j)
{
if(temp.cur[i]>b[j]-temp.cur[j]) //可以倒满
{
temp.cur[i]-=b[j]-temp.cur[j];
temp.cur[j]=b[j];
}
else //不可以倒满
{
temp.cur[j]+=temp.cur[i];
temp.cur[i]=0;
}
if(vis[temp.cur[0]][temp.cur[1]][temp.cur[2]]==0)
{
vis[temp.cur[0]][temp.cur[1]][temp.cur[2]]=1; //这种状态没有入队
temp.s++;
if(((temp.cur[0]==half&&temp.cur[1]==half)||(temp.cur[0]==half&&temp.cur[2]==half)||(temp.cur[1]==half&&temp.cur[2]==half)||(temp.cur[1]+temp.cur[2]==temp.cur[0]))&&temp.cur[0]<=b[0]&&temp.cur[1]>=0&&temp.cur[1]<=b[1]&&temp.cur[2]>=0&&temp.cur[2]<=b[2]&&(temp.cur[2]+temp.cur[1]+temp.cur[0]==b[0]))//达到一半并且不能倒出容量
{
// cout<<temp.cur[1]<<" "<<temp.cur[2]<<' '<<temp.cur[3]<<' '<<endl;
cout<<temp.s<<endl;//<<endl;
return;
}
q.push(temp);
}
}
}
}
}
}
cout <<"NO"<<endl;
}
int main()
{
while(cin>>b[0]>>b[1]>>b[2]&&b[0]&&b[1]&&b[2])
{
memset(vis,0,sizeof(vis));
vis[b[0]][b[1]][b[2]]=1;
if(b[0]%2)cout<<"NO"<<endl;
else {
half=b[0]/2;bfs();}
}
return 0;
}
城墙攻防战 wall
南山中学的校长对于绵中称霸数理化生竞赛很不爽,于是派出他们引以为豪的信奥班来攻打绵中的城墙。这项防御任务自然就落到了绵中信奥班的同学们身上。
已知绵中的城墙是由线性排列的N个石块组成,排列由1到N,每个石块都有它的防御值ai,由许多石块连成一段的城墙的防御值等于这段城墙内所有石块防御值之和乘以这段城墙内防御最低的那块石头的防御值。
经过战术商讨,绵中信奥班决定将敌人引入一段防御最高的城墙将其全歼,但是寻找出这段防御最高的城墙的问题需要他们快速解决。
单调队列维护每个的左右区间,然后前缀和一下,就可以算答案了
#include<bits/stdc++.h>
using namespace std;
long long q[1500000],h[1500000],R[1500000],r[1500000],sum[1500000];
long long n,head=1,tail=0,ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>h[i];
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+h[i];
for(int i=1;i<=n+1;i++)
{
while(h[q[tail]]>h[i]&&head<=tail)
{
R[q[tail]]=i-1;
tail--;
}
q[++tail]=i;
}
head=1;tail=0;
for(int i=n;i>=0;i--)
{
while(h[q[tail]]>h[i]&&head<=tail)
{
r[q[tail]]=i+1;
tail--;
}
q[++tail]=i;
}
for(int i=1;i<=n;i++)ans=max((sum[R[i]]-sum[r[i]-1])*h[i],ans);
cout<<ans;
return 0;
}
外星人的密码数字
XXXX年突然有外星人造访,但大家语言不通,不过科学家们经过研究发现外星人用26个英文字母组成的单词中最长不降子序列的长度来表述数字,且英文字母的排列顺序不同,现给出其排列顺序,再给出外星人说的每个数字(其实是每个英文单词,用空格隔开),翻译出外星人所说的数字(连续输出,最后加回车)。
(因为是最长不降子序列,所以数字中没有0,也就是说外星人的数字是>=1的数字)
例如
我们正常的字母排列顺序是abcdefg…….xyz,代表a<b<c<……<x<y<z
abcd efg hhh ihg四个字符串的最长不降子序列的长度分别为4 3 3 1
对于每个字符串进行最长不下降子序列就好
#include<bits/stdc++.h>
using namespace std;
map<char ,int >q;
string c;
string s;
int a[256],tep,dp[256];
inline int Dp()//最长不下降子序列
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=tep;i++)dp[i]=1;
for(int i=1;i<=tep;i++)
{
for(int j=1;j<i;j++)
{
if(a[j]<=a[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
}
return dp[tep];
}
int main()
{
cin>>c;
for(int i=0;i<c.size();i++)q[c[i]]=i+1;//对应大小
getchar();
getline(cin,s);
for(int i=0;i<s.size();i++)
{
if(s[i]==' ')//分离字符串
{
cout<<Dp();
tep=0;
continue;
}
a[++tep]=q[s[i]];
}
cout<<Dp();
return 0;
}
最短网络Agri-Net
农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过100000。
最小生成树模板
#include<bits/stdc++.h>
using namespace std;
int vis[150],a[150][150],mins[150];
int n;
int main()
{
cin>>n;
memset(mins,0x7f,sizeof(mins));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
mins[1]=0;
for(int i=1;i<=n;i++)prim
{
int k=0;
for(int j=1;j<=n;j++)
if(vis[j]==0&&(mins[j]<mins[k]))k=j;
vis[k]=1;
for(int j=1;j<=n;j++)
if(vis[j]==0&&(mins[j]>a[k][j]))
mins[j]=a[k][j];
}
int ans=0;
for(int i=1;i<=n;i++)ans+=mins[i];
cout<<ans;
return 0;
}
二叉苹果树
有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树
2 5
\ /
3 4
\ /
1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
树形dp的模板题,先是以一为根节点递归建树,然后dp[i][j]表示以i为根节点保留j根树枝最大保留的苹果数量。
一重循环枚举给左儿子的树枝数目,右儿子可以减出,但是要-1,因为连这个点还要一条边
#include<bits/stdc++.h>
using namespace std;
struct tr
{
int lr;
int rr;
int z;
}tree[150];
int a[150][150],q,n,dp[150][150];
bool vis[150];
void make(int x)
{
vis[x]=1;
for(int i=1;i<=n;i++)//建树
{
if(vis[i]==0&&a[x][i]!=-1)
{
if(tree[x].lr==0)tree[x].lr=i;
else tree[x].rr=i;
tree[i].z=a[x][i];
make(i);
}
}
}
int f(int x,int y)//dp
{
if(dp[x][y]!=-1)return dp[x][y];
if(x==0||y==0)
{
dp[x][y]=0;
return dp[x][y];
}
dp[x][y]=0;
for(int i=0;i<y;i++)
{
int zr=f(tree[x].lr,i);
int yr=f(tree[x].rr,y-1-i);
dp[x][y]=max(dp[x][y],zr+yr+tree[x].z);
}
return dp[x][y];
}
int main()
{
cin>>n>>q;
q++;
memset(a,-1,sizeof(a));
memset(dp,-1,sizeof(dp));
for(int i=1;i<n;i++)
{
int x,y,z;
cin>>x>>y>>z;
a[x][y]=a[y][x]=z;
}
make(1);
cout<<f(1,q);
return 0;
}