目录 |
我就是入门半年,还没入门成功的大——
从现在起开始认真刷紫薯(其实是抄紫薯哈哈哈)
每道题的理解都在代码里面。
我是真的弱,如果我有错误,评论里留下你的大名,我一定记在我的报答簿上。
正文 |
让我们一起————入门到入土
话不多说,立马进入正题
不愧是大佬,思路如此清晰
后知后觉——好像应该挂出链接——戳我戳我
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 100007
typedef long long ll;
//紫薯上的题
//就是看出栈顺序能够不能实现
//1。检测此时该入栈的数是否对
//2.检测栈顶的数是否对
//3.两者都不满足就入栈(这样就可以调换顺序)
//tips:为了学好思路,这些都是请参考过紫薯的,不过代码是自己打的
int main(){
int a,c[1006],i,t;
while(cin>>a&&a!=0){
while(1){
t=0;
cin>>c[1];
if(c[1]==0)
break;
for(i=2;i<=a;i++)cin>>c[i];//这是要求最后的顺序
stack<int>w;
int e=1,f=1;
while(e<=a)/*全部输出都能够一一对应*/{
if(f==c[e])e++,f++;//满足1
else if(!w.empty()/*栈里有东西*/&&w.top()==c[e]){
w.pop();
e++;
}
else if(f<=a)/*保证还可以入栈,就可以换顺序*/{
w.push(f);
f++;
}
else /*上面都不满足就不可能了*/{t=1;break;}
}
printf("%s\n",t==0?"Yes":"No");
}
cout<<endl;//注意输出格式
}
return 0;
}
代码抽风还请指证,有啥直接告诉我,我家就住在CSDN
构造函数的使用
(**其实我是才学过,但是没用过,也不知道怎么用 **)
废话不多说上代码(题目通道)
图片大小调不了吗?
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 100007
typedef long long ll;
//还是紫薯上的题
//还是用栈实现
/*思路就是字母也入栈,一遇到“)”就进行判断
判断此时顶端的两个乘,如果不能乘,就直接错误,输出error
否则,sum加上,然后把两个相乘后矩形重新放入栈,其实整个过程
没有“()”入栈的情况,嘿嘿
思路清晰,不愧是大佬*/
struct www{
int hang;
int lie;
www(int hang=0,int lie=0):hang(hang),lie(lie){}//这个就是精华,我见都没见过,后面有大作用
//猛男醒悟,这就是我才学的构造函数,居然结构体内也可以用,我还一直以为没有用
}c[30];//记录矩阵
int main(){
int a,i,sum,t;
char b;
cin>>a;
while(a--){//朴素的输入
cin>>b;
cin>>c[b-'A'].hang>>c[b-'A'].lie;
}
string s;
stack<www>k;//建立栈
while(cin>>s){
sum=0,t=0;
for(i=0;i<s.size();i++){
if(isalpha(s[i]))k.push(c[s[i]-'A']);//是字母就入栈
else if(s[i]==')'){//"("没有用,牛逼,大佬
//我看紫薯上的程序也没判断出栈时元素不足两个的情况
www p=k.top();k.pop();
www q=k.top();k.pop();//取出最上面两个
if(q.lie!=p.hang){t=1;break;}//注意顺序
else{
sum+=q.hang*q.lie*p.lie;
k.push(www(q.hang,p.lie));/*这就是精华,怎样通过,q.a,p.b
再放入栈还*/
}
}
}
if(t==1)cout<<"error"<<endl;
else cout<<sum<<endl;
}
return 0;
}
我写代码真是一点都不好看
罪过罪过
慢着,我好像明白了这个构造函数
它其实就是用传进去的两个参数重新构造了一个www变量
字母只是一个表壳,内在就是这个www变量
ohhhhhhhhhhhhhhhhhh
本来使用链表,
可我不想用
其实是我看了好久,都想不通
就屈服于STL的伟大,用list
感觉确实有点慢,链表实现我下次补。
下次一定
血池
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 100007
typedef long long ll;
//还是紫薯上的题
//无奈本人太菜鸡,这个链表怎么也想不通
//就先放过自己,用list
//早知道就用这个了,又浪费傻子几小时
int main(){
int i;
string s;
while(cin>>s){
list<char>w;
list<char>::iterator it =w.begin();
for(i=0;i<s.size();i++){
if(s[i]=='['){
it=w.begin();//从前面
}
else if(s[i]==']'){
it=w.end();//从后面
}
else{//进入list
w.insert(it,s[i]);
}
}
for(it=w.begin();it!=w.end();it++)cout<<*it;
cout<<endl;
}
return 0;
}
对不住了。我看了一下下面一道链表,好像更难
只有先行略过了
菜鸡跳题,毫不犹豫
假装**第四滴血**
大树
周树人
“抓捕周树人和我鲁迅有什么关系”
小球下落
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 100007
typedef long long ll;
/*
//二叉树,简单模拟
int c[1<<20];//1向左移动20位就相当于pow(2,20);
//最大节点个数[(1<<20)-1]
int main(){
int a,b,k;
while(scanf("%d%d",&a,&b)==2){
memset(c,0,sizeof(c));//开关清零
int maxmax=(1<maxmax)break;
}
}
//出界之前的位置
cout<
//上面的会超时,应为他每一个小球都模拟了,浪费了大量时间
//其实我们只用知道小球的编号就可与以知道他的路径
//当在1节点时,编号为偶数,就会走右边,否则走左边
//每个节点都是这个道理
int main(){
int a,b,k,p;
cin>>p;
while(p--){
cin>>a>>b;
k=1;
a--;//只能下降a-1次
while(a--){
if(b%2==1){
k*=2;//左树比右树多走一个 ,因为每次先走左数
b=(b+1)/2;
}
else{
k=k*2+1;
b/=2;
}
}
cout<<k<<endl;
}
cin>>p;
return 0;
}
前面跳了两道题,
都是构造树的?没看明白
**菜鸡跳题,毫不犹豫 **
跳的两道题
第一道
第二道
紫薯的思路真的好清晰
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 100007
typedef long long ll;
//紫薯继续
//看题是递归输入,就是个模拟
bool sou(int &k){//才学的引用 ,k值可以进行修改
int e,f,g,h;
bool p=true;
bool q=true;
cin>>e>>f>>g>>h;
if(e==0){//左手是个子树
p=sou(e);//记录左边的重量 ,看左边是不是平衡的
}
if(g==0){
q=sou(g);//记录右边的重量 ,看右边是不是平横的
}
k=e+g;
return q&&p&&f*e==g*h;//左右和自己都平衡才是平衡
}
int main(){
int a,k=0;
cin>>a;
while(a--){
if(sou(k))
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
if(a)cout<<endl;
}
return 0;
}
有两个小坑点就是
1.函数记得要有返回值
2.UVA的题对于输出格式好像很严格,行尾有空格都不行
紫薯上就没有没有,导致一直RE
题目传送门
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 100007
typedef long long ll;
//紫薯继续
//多用bool类型
int c[200];
//储存每个水平面的值
void jian(int a){
int b;
cin>>b;
if(b!=-1)
c[a]+=b;
else
return ;//等于-1就是没有路了,直接返回
jian(a-1);//先左
jian(a+1);//后右
}
bool du(){//对第一个节点进行特殊处理
int a;
cin>>a;
if(a==-1)
return false;
memset(c,0,sizeof(c));//因为多组输入
c[200/2]=a;//第一个节点处于最中间的位置
jian(200/2-1);//先左
jian(200/2+1);//后右
return true;
}
int main(){
int t=1;
while(du()){
int i=0;
while(c[i]==0)i++;
cout<<"Case "<<t++<<":"<<endl<<c[i++];
while(c[i]!=0)cout<<" "<<c[i++];
cout<<endl<<endl;
}
return 0;
}
Kahn算法实现
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
typedef long long ll;
// 紫薯继续(和平精英千年老二)
//拓扑排序
int du[106],a,b;//记录每个点的入度
vector<int>c[106];//邻接表
int ans[106];//记录答案
void sou(){
int k=0,i;
queue<int>p;
for(i=1;i<=a;i++)//寻找出度为0的点
if(du[i]==0)
p.push(i);/*放进队列,因为每次
开始处理都是从入度为0点开始*/
while(!p.empty()){
int xin=p.front();
p.pop();
ans[++k]=xin;//记录答案
/*上面清除了一个入度为0的点,
下面要把与之所相连的得点入度给剪掉 */
for(i=0;i<c[xin].size();i++){
du[c[xin][i]]--;
if(du[c[xin][i]]==0){
/*如果这时候产生一个新的
入度为0 的点 */
p.push(c[xin][i]);
}
}
}
for(i=1;i<=a;i++){
if(i==1)
cout<<ans[i];
else
cout<<" "<<ans[i];
}
cout<<endl;
// if(k!=a)//说明这个环
// return 0;
// else
// return 1;
}
int main(){
int e,f;
while(scanf("%d%d",&a,&b)==2){
if(a==b&&b==0)
break;
for(int i=1;i<=a;i++){
c[i].clear();//临接表清空
}
memset(du,0,sizeof(du));
while(b--){
cin>>e>>f;
du[f]++;//先完成e,才能再完成f
c[e].push_back(f);//e->f
}
sou();
}
return 0;
}
也可以DFS实现
欧拉回路
本题思路
//用并查集实现
//1.要查询每个点的祖先,有多个祖先的就无法形成通路(判断通路)
//2.有两个点的出度与入度不相等,且相差1。或者每个点出度入度都等
例如 asdsdb bsadjasjda
这个UVa今天交不上去啊
先把代码挂在这里
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
typedef long long ll;
// 紫薯继续(和平精英千年老二)
//把单词看成有向边,单词开头末尾看作节点
//用并查集实现
//1.要查询每个点的祖先,有多个祖先的就无法形成通路(判断通路)
//2.有两个点的出度与入度不相等,且相差1
int f[30];//记录祖先节点
int ru[30],chu[30],book[30];
int zu(int a){//寻找祖先节点
if(a!=f[a])
return f[a]=zu(f[a]);
else
return a;
}
void chushihua(){
memset(ru,0,sizeof(ru));
memset(chu,0,sizeof(chu));
memset(book,0,sizeof(book));
for(int i=1;i<=26;i++){
f[i]=i;
}
}
int main(){
int a,b,i,ans,r,ch,t;
cin>>a;
while(a--){
r=ch=t=ans=0;
chushihua();
cin>>b;
while(b--){
string s;
cin>>s;
int p=s[0]-'a'+1;
int q=s[s.size()-1]-'a'+1;
ru[p]++;
chu[q]++;
int w=zu(p);
int z=zu(q);
if(w!=z)
f[z]=w;//合并
book[p]=book[q]=1;//记录有这个点
}
for(i=1;i<=26;i++){
if(book[i]){
/*看看有几个祖先节点,
只有一个才能形成通路*/
if(f[i]==i)
ans++;//祖先节点+1
if(ru[i]!=chu[i]){
if(ru[i]-chu[i]==1)
r++;
else if(chu[i]-ru[i]==1)
ch++;
//合法的出入度不同就这两种,其他都不正确
else
t=1;
}
//判段
if(t||ans>1){
break;
}
}
}
if(i!=27)
cout<<"The door cannot be opened."<<endl;
else
{
if((r==1&&ch==1)||(r==ch&&r==0))
cout<<"Ordering is possible."<<endl;
else
cout<<"The door cannot be opened."<<endl;
}
}
}
只用枚举前5个数,后五个数会自动形成
最后再判断一下数字是否重复
前排提示:UVA对输出格式有控制,WA死我了
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
typedef long long ll;
// 紫薯继续(和平精英千年老二)
//暴力也需要优化!
/*直接枚举分子就行,
分母也可以得出,
最后看看有没有重复就行*/
int book[12],a,sum=0,book1[12],t;//两个book,标记两个数
void sou(int b){
if(b==6)
{
if(sum%a!=0)
return ;//找不到整数被除数
int k=sum/a;
int q=k;/*记录这个数,
因为后面判段数字是否重复时会改变
如果判断没有错误,就可以直接输出*/
//分解k,看是否有元素重合
memset(book1,0,sizeof(book1));
for(int i=1;i<=5;i++){
if(book[k%10]==1||book1[k%10]==1)//出现过了
return ;
book1[k%10]=1;
k/=10;
}
t++;
printf("%05d / %05d = %d\n",sum,q,a);
return ;
}
//枚举
for(int i=0;i<=9;i++){
int p=sum;
if(book[i]==0){
sum=sum*10+i;
book[i]=1;
sou(b+1);
sum=p;
book[i]=0;
}
//恢复原来状态
}
}
int main(){
int w=0;//执行的次数,因为最后输出不带\n
while(scanf("%d",&a)&&a){
if(w)
cout<<endl;
w++;
memset(book,0,sizeof(book));
t=0;
sou(1);
if(t==0)
printf("There are no solutions for %d.\n",a);
}
return 0;
}
数据比较小,也就是个枚举
枚举起点和终点
终点动态变换,起点固定
两重循环就可以了
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
typedef long long ll;
// 紫薯继续(和平精英千年老二)
//就是枚举,没别的
ll c[20];
int main(){
ll a,i,j,b;
ll sum,maxmax,k=0,p;
while(scanf("%lld",&a)!=EOF){
maxmax=0;
for(i=1;i<=a;i++)
scanf("%lld",&c[i])A;
for(i=1;i<=a;i++){//起点
sum=1;
for(j=i;j<=a;j++){//终点移动
sum*=c[j];
maxmax=max(sum,maxmax);
}
}
printf("Case #%d: The maximum product is %lld.\n\n", ++k, maxmax);
}
return 0;
}
数学分析走着
解不等式
x=>2a>=y
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
typedef long long ll;
// 紫薯继续(和平精英千年老二)
//数学题?
//解不等式——解出 x=>2a>=y
int c1[10000];
int main(){
int a,y,p;
while(scanf("%d",&a)!=EOF){
p=0;
for(y=1;y<=2*a;y++){//y的取值范围
//式子变换——a=xy/(x+y)
//x=ay/(y-a)
if(y-a>0&&(a*y)%(y-a)==0&&(a*y)/(y-a)!=0){
c1[++p]=y;//记录答案
}
}
cout<<p<<endl;
for(int i=1;i<=p;i++)
printf("1/%d = 1/%d + 1/%d\n",a,(a*c1[i])/(c1[i]-a),c1[i]);
}
return 0;
}
前排恭喜vj上UVa连炸两天 |
素数环
回溯法
记住最后还要和环首 1 进行判断
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
typedef long long ll;
//素数处理
int book[20];//标记数组
int ans[20];//记录答案
int su[50];//记录素数
int a;
void sushu(){
for(int i=1;i<=50;i++){
for(int j=2;j*j<=i;j++){
if(i%j==0){
su[i]=1;//素数标记为0
break;
}
}
}
}
void sou(int b){
if(b==a+1){
if(su[ans[b-1]+1]==0){//因为是环状,所以还要与开头 1 判断
for(int i=1;i<=a;i++){
if(i!=a)//注意输入输出格式
cout<<ans[i]<<" ";
else
cout<<ans[i];
}
cout<<endl;
return ;
}
}
for(int i=2;i<=a;i++){
if(book[i]==0&&su[i+ans[b-1]]==0){
book[i]=1;
ans[b]=i;
sou(b+1);
//回溯
book[i]=0;
}
}
}
int main(){
int k=0;
sushu();
while(scanf("%d",&a)!=EOF){
if(k!=0)
cout<<endl;//注意输入输出格式
printf("Case %d:\n",++k);
//第一个数为 1
ans[1]=1;
sou(2);
}
}
UVa的输入输出格式是真的难搞
注意子串相等时是怎样比较的
还是回溯法
每次添加一个字母进入字符串,最终顺序就是升序
我有点写不明白,还是自己看吧
紫薯上的一句话:
回溯法中,
应注意不必要的判断,
就像八皇后,每次只用判断新皇和之前的皇是否冲突,而不用判断以前的皇后是否冲突,因为以前已经判断过了
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
//这紫薯看的我天天喊牛皮
//回溯的思想
//每次添加一个字母
int w=0;//记录第几个困难串
int a,b,t;
char ans[100];//记录答案
int jian(int c){
int mid=c/2;//枚举比较的字串长度
int i,j,p;
for( i=1;i<=mid;i++){
for( j=c+1-2*i,p=c+1-i;p<=c;j++,p++){//两个字串比较是否相等
if(ans[j]!=ans[p])//字串不相等
break;
}
if(p==c+1)//字串相等
return 0;
}
return 1;
}
void dfs(int c){
if(t==1)
return ;
if(++w==a){/*每次只要进行了dfs函数都会
产生一个新的排列
所以w++;*/
t=1;
//令人烦躁的输出
for(int i=1;i<c;i++){
cout<<ans[i];
if(i%4==0&&i!=c-1){
if(i%64==0)
cout<<endl;
else
cout<<" ";
}
}
cout<<endl<<c-1<<endl;
return ;
}
for(int i=0;i<b;i++){
ans[c]=i+'A';
if(jian(c))//如果检查没有重复,就继续下一个
dfs(c+1); //只要继续dfs,就是产生了一个排列
//否则就重新选择
}
}
int main(){
while(scanf("%d%d",&a,&b)&&b&&a){//平平无奇的输入
w=-1;
t=0;
dfs(1);
}
return 0;
}
这两个题我愣是没读懂题
烦躁
代码是我抄的,加了点解释
看着比紫薯上好懂
虽然长了点,但有一大段“重复的”
#include
#include
#include
#include
/*看紫薯的题解有点抽风,我的木鱼脑袋接受不了
找的其他题解 */
using namespace std;
const int MAXN = 200;
int a, b, c, d, maxd1, minamount;
bool notvist[MAXN+1][MAXN+1][MAXN+1];
struct node {//记录每一个状态
int a, b, c, amount;
bool operator < (const node& n) const {
return amount > n.amount;
}//这个是干嘛的??????????????????
};
int bfs()
{
maxd1 = 0;
minamount = 0;
//因为是对移动水最少的进行扩展,所以使用优先队列
priority_queue<node> q;
//多组输入,标记数组清零
memset(notvist, true, sizeof(notvist));
node f, v;
f.c = c;
f.a = 0;
f.b = 0;
f.amount=0;
//标记初始状态
q.push(f);
notvist[f.c][f.a][f.b] = false;
//下面进行扩展
while(!q.empty()) {
f = q.top();//优先队列这里是top
q.pop();
if(f.a == d || f.b == d || f.c == d)
return f.amount;
//找到结果,直接退出dfs
if(f.a < d && f.a > maxd1) {
maxd1 = f.a;
minamount = f.amount;
}
if(f.b < d && f.b > maxd1) {
maxd1 = f.b;
minamount = f.amount;
}
if(f.c < d && f.c > maxd1) {
maxd1 = f.c;
minamount = f.amount;
}
/*用三个if寻找倒在杯子里
的不大于h的最大值
顺便记录步数 */
// 下面枚举6中操作,大同小异
//用v记录操作后的状态
// c --> a
if(f.c && a - f.a > 0) {
if(f.c > a - f.a) { // c > a的剩余容量
v.c = f.c - (a - f.a);
v.a = a;
v.b = f.b;
v.amount = f.amount + (a - f.a);
} else { // c <= a的剩余容量
v.c = 0;
v.a = f.a + f.c;
v.b = f.b;
v.amount = f.amount + f.c;
}
if(notvist[v.c][v.a][v.b]) {
notvist[v.c][v.a][v.b] = false;
q.push(v);
}
}
// c --> b
if(f.c && b - f.b > 0) {
if(f.c > b - f.b) { // c > b的剩余容量
v.c = f.c - (b - f.b);
v.a = f.a;
v.b = b;
v.amount = f.amount + (b - f.b);
} else { // c <= b的剩余容量
v.c = 0;
v.a = f.a;
v.b = f.b + f.c;
v.amount = f.amount + f.c;
}
if(notvist[v.c][v.a][v.b]) {
notvist[v.c][v.a][v.b] = false;
q.push(v);
}
}
// a --> c
if(f.a && c - f.c > 0) {
if(f.a > c - f.c) { // a > c的剩余容量
v.c = c;
v.a = f.a - (c - f.c);
v.b = f.b;
v.amount = f.amount + (c - f.c);
} else { // a <= c的剩余容量
v.c = f.c + f.a;
v.a = 0;
v.b = f.b;
v.amount = f.amount + f.a;
}
if(notvist[v.c][v.a][v.b]) {
notvist[v.c][v.a][v.b] = false;
q.push(v);
}
}
// a --> b
if(f.a && b - f.b > 0) {
if(f.a > b - f.b) { // a > b的剩余容量
v.c = f.c;
v.a = f.a - (b - f.b);
v.b = b;
v.amount = f.amount + (b - f.b);
} else { // a <= b的剩余容量
v.c = f.c;
v.a = 0;
v.b = f.b + f.a;
v.amount = f.amount + f.a;
}
if(notvist[v.c][v.a][v.b]) {
notvist[v.c][v.a][v.b] = false;
q.push(v);
}
}
// b --> c
if(f.b && c - f.c > 0) {
if(f.b > c - f.c) { // b > c的剩余容量
v.c = c;
v.a = f.a;
v.b = f.b - (c - f.c);
v.amount = f.amount + (c - f.c);
} else { // b <= c的剩余容量
v.c = f.c + f.b;
v.a = f.a;
v.b = 0;
v.amount = f.amount + f.b;
}
if(notvist[v.c][v.a][v.b]) {
notvist[v.c][v.a][v.b] = false;
q.push(v);
}
}
// b --> a
if(f.b && a - f.a > 0) {
if(f.b > a - f.a) { // b > a的剩余容量
v.c = f.c;
v.a = a;
v.b = f.b - (a - f.a);
v.amount = f.amount + (a - f.a);
} else { // b <= a的剩余容量
v.c = f.c;
v.a = f.a + f.b;
v.b = 0;
v.amount = f.amount + f.b;
}
if(notvist[v.c][v.a][v.b]) {
notvist[v.c][v.a][v.b] = false;
q.push(v);
}
}
}
return -1;
}
int main()
{
int t;
scanf("%d", &t);
while(t--) {
scanf("%d%d%d%d", &a, &b, &c, &d);
int ans = bfs();
if(ans < 0) {
printf("%d %d\n", minamount, maxd1);
} else
printf("%d %d\n", ans, d);
}
return 0;
}
双向BFS
不会
真的佩服看了大量题解看不懂,突然有个大佬的题解明白了
在线跪
牛逼
总结一下
这是那个大佬的题解链接
什么狗屁迭代加深搜索,看的我懵逼了一早上
这个也没涉及什么算法,所以看了还是好懂,但是叫我自己写,肯定写不出来
题目最重要的是每次搜索规定了上下界
还有好多小细节:多用乘法代替除法…
大佬真强
我改了一些细节
#include
#include
#include
#include
#include
#include
//真的是难,我要吐了
//真的是半天一道题,还是看的别人的代码
//大佬思路
using namespace std;
const int N=1e5+7;
set <long long> p;
long long t,A,B,n,mxdep;
long long ans[N],w[N];
bool flag;
//inline long long gcd(long long a,long long b)
//{
// return b ? gcd(b,a%b) : a;
//}//gcd是为了约分
inline bool pd()
{
for(int i=mxdep;i;i--)
if(ans[i]!=w[i])
return ans[i]&&ans[i]<w[i];
return 0;
}//判断答案是否更优
inline void dfs(long long dep,long long a,long long b,long long mi)
{
if(dep==mxdep)//还剩最后一个数时
{
if(b%a||(b*mi<a)||p.count(b/a)) return;//判断合法性
//不合法
/*判断合法性的三个条件
1.可以构成1/n形式
2.比mi小
3.b/a这个数可以使用
*/
//如果合法还要看是否为最优情况
w[dep]=b/a;
if(pd()) return;//判断答案是否更优
memcpy(ans,w,sizeof(w)); flag=1;//更新
return;
}
mi=max(mi,b/a+1);//求出下界
for(long long i=mi;i;i++)
{
if( (mxdep-dep+1)*b<=i*a ) return;
/*因为每个选择的
数其实大小是递减的,如果这个数和以后的数
分母都用i的和都比a/b小,那就肯定实现不了
后面更大的i就更不用再考虑了*/
/*用乘法表示的的优势在于不用
害怕除法的精度丢失*/
if(p.count(i)) continue;//判断合法
w[dep]=i;//记录路径
long long xa=a*i-b,xb=i*b;//通分
// long long z=gcd(xa,xb);
dfs(dep+1,xa,xb,i+1);//i改变因为i是递增的
}
}
int main()
{
cin>>t;
for(long long i=1;i<=t;i++)
{
flag=0; mxdep=1;
cin>>A>>B>>n;
long long c;
p.clear();//细节
while(n--) scanf("%lld",&c),p.insert(c);//存储不合法的数
while(mxdep++)//分裂成k个数就可以找到答案,从2开始
{
memset(ans,0,sizeof(ans));
dfs(1,A,B,B/A+1);
if(flag) break;
}//迭代加深
printf("Case %lld: %lld/%lld=1/%lld",i,A,B,ans[1]);
for(int i=2;i<=mxdep;i++)
printf("+1/%lld",ans[i]);
printf("\n");
}
return 0;
}
什么IDA算法了,过
what??????????????
标准的动态规划,容易实现
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
int a,ans[102][102],c[102][102];
int sou(int x,int y){
if(x==a){//最后一层
return ans[x][y]=c[x][y];
}
else{
if(ans[x][y]!=0)
return ans[x][y];
else{
return ans[x][y]=c[x][y]+max(sou(x+1,y),sou(x+1,y+1));
}
}
}
int main(){
cin>>a;
for(int i=1;i<=a;i++){
for(int j=1;j<=i;j++){
cin>>c[i][j];
}
}
cout<<sou(1,1);
}
有向无环图,不清楚DAG的可以博客查一下
感觉和上一道数字三角形差不多
就是
1.记忆化
2.建立有向图
3.保证每个点都有ans值
4.详细的写在代码里了
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define mid 1000000007
//有向无环图
int b,ans[1006],xian[1006][1006],c[1006][2];
int dp(int a){
int i,maxmax;
if(ans[a]>0)
return ans[a];//记忆化
maxmax=1;//注意初值设置为1
for(i=1;i<=b;i++){
if(xian[a][i])//如果有向边存在
maxmax=max(dp(i)+1,maxmax);
}
return ans[a]=maxmax;
}
int main(){
int a,maxmax,i,j;
cin>>a;
while(a--){
memset(ans,0,sizeof(ans));
memset(xian,0,sizeof(xian));
cin>>b;
for(i=1;i<=b;i++)cin>>c[i][0]>>c[i][1];
//建立有向图
for(i=1;i<=b;i++){
for(j=1;j<=b;j++){
if((c[i][0]>c[j][0]&&c[i][1]>c[j][1])||(c[i][1]>c[j][0]&&c[i][0]>c[j][1])){
xian[i][j]=1;//注意是j可以放在i里面
}
}
}
maxmax=-1;
//注意下面这样做的理由是
//让每一个点肯定都会得到ans
for(i=1;i<=b;i++){
maxmax=max(maxmax,dp(i));//找出最大的那个ans
}
cout<<maxmax<<endl;
}
return 0;
}
这题我都找了好久
不用引用也能错?我吐了
代码是我网上cv的,我的有个样例超时,恶心死我
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int k = 0x3f3f3f3f;//最大最小值
int ans[10006], c[106], book[10006], a, b;//book数组用来记忆化
int da(int x) {
if (book[x])
return ans[x];
book[x] = 1;
int &maxmax = ans[x];//不用引用就完蛋
maxmax = -k;
for (int i = 1; i <= a; i++) {
if (x >= c[i]) {
maxmax = max(maxmax, da(x - c[i]) + 1);
}
}
return ans[x] = maxmax;
}
int xiao(int x) {
if (book[x])
return ans[x];
book[x] = 1;
int &minmin = ans[x];
minmin = k;
for (int i = 1; i <= a; i++) {
if (x >= c[i]) {
minmin = min(minmin, xiao(x - c[i]) + 1);
}
}
return minmin;
}
int main() {
cin >> a >> b;
for (int i = 1; i <= a; i++) {
cin >> c[i];
}
book[0] = 1;
ans[0] = 0;
cout << xiao(b);
memset(book, 0, sizeof(book));
book[0] = 1;
ans[0] = 0;
cout << " " << da(b);
}
和前面的矩形嵌套大同小异
每个立方体产生三种摆放情况,然后就是标准的记忆化搜索
struct www{
int x;int y;int z;
}c[200];//结构体储存每种摆放方式
int ans[200],a;
int dp(int p){//标准的记忆化搜索
int i,maxmax;
if(ans[p]){
return ans[p];
}
maxmax=c[p].z;/*记住初始值设置为自己的长度,
最短就是没有可以放在上面的,最小值就是自己*/
for(i=1;i<=a*3;i++){
if((c[i].x<c[p].x&&c[i].y<c[p].y)||(c[i].x<c[p].y&&c[i].y<c[p].x)){
maxmax=max(maxmax,dp(i)+c[p].z);
}
}
return ans[p]=maxmax;
}
int main(){
int i,p,e,f,g,k=0;
while(scanf("%d",&a)&&a){
p=0;
memset(ans,0,sizeof(ans));
for(i=1;i<=a;i++){
cin>>e>>f>>g;
c[++p].x=e,c[p].y=f,c[p].z=g;
c[++p].x=f,c[p].y=g,c[p].z=e;
c[++p].x=g,c[p].y=e,c[p].z=f;
//每个立方体三种摆放方式
}
int maxmax=0;
for(i=1;i<=a*3;i++){
maxmax=max(dp(i),maxmax);
}
cout<<"Case "<<++k<<": maximum height = "<<maxmax<<endl;
}
return 0;
}
就是一个DP
注意细节的处理
代码又是一直WA
带权值的dp?就是以前dp的+1变成了+实质的值
int c[103][2], a, b;
int dp[50006];
int sou(int x);
int main() {
memset(dp, -1, sizeof(dp));
cin >> a >> b;
for (int i = 0; i < a; i++) {
cin >> c[i][0] >> c[i][1];
}
cout<<sou(b);
return 0;
}
int sou(int x) {
if (dp[x] != -1)
return dp[x];
int maxmax = 0;
for (int i = 0; i < a; i++) {
if (x >= c[i][0]) {
maxmax = max(maxmax, c[i][1] + sou(x - c[i][0]));
}
}
return dp[x] = maxmax;//记忆化
}
典型的0-1背包
代码是看了别人的,感觉还是没法独立写出来
int a, b, c[101][2],ans[10006];
int main() {
cin >> a >> b;
for (int i = 1; i <= a; i++) {
cin >> c[i][0] >> c[i][1];
}
for (int i = 1; i <= a; i++) {
for (int j = b; j >=c[i][0]; j--) {//保证可以往包里拿
ans[j] = max(ans[j],ans[j-c[i][0]]+c[i][1]);//加上前面的效果
}
}
cout << ans[b];
}
可算AC了
也算典型的01背包问题了,时间其实最大是有范围的。
注意最后要留一秒才能唱金曲,详细解释写在代码里了
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//01背包
int k[52];
int ans[10000];//时间是有范围的
int main() {
int a, b, c, z = 0;//z是输出时用的
cin >> a;
while (a--) {
memset(ans, -1, sizeof(ans));
cin >> b >> c;
for (int j = 1; j <= b; j++) {
cin >> k[j];
}//平凡的输入
ans[0] = 0;
for (int i = 1; i <= b; i++) {//物品
for (int j = c; j >=k[i]; j--) {//时间
if (ans[j-k[i]] != -1)//确保j-k[i]在这个时间唱歌有可能实现
ans[j] = max(ans[j], ans[j - k[i]] + 1);
}
}
int maxmax = -1, p ;
for (int i = 0; i < c; i++) {//等于c的情况特殊处理,因为c时无法唱《金曲》
if (ans[i] >=maxmax) {//找最大的ans,并且时间i也要大的,所以有等号
maxmax = ans[i];
p = i;
}
}
if (maxmax + 1 > ans[c] || (maxmax + 1 == ans[c]&&p+678>c))
//条件判断
printf("Case %d: %d %d\n", ++z, maxmax + 1, p + 678);
else
printf("Case %d: %d %d\n", ++z, ans[c], c);
}
return 0;
}
只写了最智障的写法,其他的还不懂,后面再来
//最智障的n^2
int dp[1006],c[1006];
int main() {
int a;
cin >> a;
for (int i = 1; i <= a; i++) {
cin >> c[i];
dp[i] = 1;
}
for (int i = 1; i <= a; i++) {
for (int j = 1; j < i; j++) {
if (c[i] > c[j]) {//比前面的数大,才可能形成上升序列
dp[i] = max(dp[i], dp[j] + 1);
/*表示以i结尾的最长上升序列,
所以后面还有个遍历寻找最大*/
}
}
}
int ans = 0;
for (int i = 0; i <= a; i++) {
ans = max(ans, dp[i]);
}
cout << ans;
return 0;
}