重视自底向上思想的有力证明——Legacy Code


来源题解目录
比赛link

Unsolved 19 / 80 H Gym 100753H Legacy Code

题意应该是:
操作中要有::PROGRAM至少一个才有用
操作可以嵌套
问有几个无用的操作

Sample Input 1
2
SuperGame::PROGRAM 0
HelpPackage::HelpFunction 2
HelpPackage::HelpFunction SuperGame::PROGRAM

Sample Output 1
0

Sample Input 2
2
Loop::CallA 1
Loop::CallB
Loop::CallB 1
Loop::CallA

Sample Output 2
2

Sample Input 3
2
SuperGame::PROGRAM 1
SuperServer42::PROGRAM
SuperServer42::PROGRAM 1
SuperServer42::PROGRAM

Sample Output 3
0

应该是模拟题?
我超时了
思路是dfs找

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
//#include<iostream>
//#include<algorithm>
#include<string>
//#include<sstream>
//#include<vector>
#include<map>
//#include<set>
//#include<ctype.h>
//#include<stack>
//#include<queue>
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define _forplus(i,a,b) for( register int i=(a); i<=(b); i++)
#define _forsub(i,a,b) for( register int i=(a); i>=(b); i--)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define INF 0x3f3f3f3f
#define pi (acos(-1))
#define N 405
struct Node{
	int flag=0;//默认无效 
	char it[N];//存操作 读入 
	int k;//子个数 读入 
	char cto[N][N];//子去处名称 读入 
}node[N];
map<string,int>m;//子去处记录 读完出 小心死循环超时 
//注意:char*作为指针,是不能判断字符串的,只能判断存放地址 
//并且string 可以兼容 char[] ,我只修改了这里   
int vis[N];//看每一个时防止死循环 
int n,cnt=0;
bool jd(char*str){
	char*p=strchr(str,':')+2;
	//printf("%s\n",p);
	if(strcmp(p,"PROGRAM")==0)return true;
	else return false;
}//判断该字符串是否能有效 
bool bfs(int now){
	if(jd(node[now].it)){
		return true;
	}else{
		vis[now]=1;
		_forplus(i,1,node[now].k){
			int sx=m[node[now].cto[i]];
			if(!vis[sx]){
				if(bfs(sx)){
					return true;
				}
			}
		}
	}
	return false;
	
}
int main(){
	scanf("%d",&n);
	_forplus(i,1,n){
		scanf("%s",node[i].it);
		scanf("%d",&node[i].k);
		_forplus(j,1,node[i].k){
			scanf("%s",node[i].cto[j]);
		}
	}
	_forplus(i,1,n){
		m[node[i].it]=i;
	}
	_forplus(i,1,n){
		mem(vis,0);
		if(bfs(i)){
			cnt++;
		}
	}
	printf("%d",n-cnt);
	return 0;
}

错是不会错的,但是有很多重复计算
我要用上标记这个是否是,减少重复计算

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
//#include<iostream>
//#include<algorithm>
#include<string>
//#include<sstream>
//#include<vector>
#include<map>
//#include<set>
//#include<ctype.h>
//#include<stack>
//#include<queue>
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define _forplus(i,a,b) for( register int i=(a); i<=(b); i++)
#define _forsub(i,a,b) for( register int i=(a); i>=(b); i--)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define INF 0x3f3f3f3f
#define pi (acos(-1))
#define N 405
struct Node{
	int flag=0;//默认0 有效1 无效-1 减少重复 
	char it[N];//存操作 读入 
	int k;//子个数 读入 
	char cto[N][N];//子去处名称 读入 
}node[N];
map<string,int>m;//子去处记录 读完出 小心死循环超时 
//注意:char*作为指针,是不能判断字符串的,只能判断存放地址 
//并且string 可以兼容 char[] ,我只修改了这里   
int vis[N];//看每一个时防止死循环 
int n,cnt=0;
bool jd(char*str){
	char*p=strchr(str,':')+2;
	//printf("%s\n",p);
	if(strcmp(p,"PROGRAM")==0)return true;
	else return false;
}//判断该字符串是否能有效 
bool bfs(int now){
	if(node[now].flag==1){
		return true;
	}
	if(node[now].flag==-1){
		return false;
	}//flag减少重复 
	if(jd(node[now].it)){
		node[now].flag=1;
		return true;
	}else{
		vis[now]=1;
		_forplus(i,1,node[now].k){
			//printf("in %d order: %d\n",now,i);
			int sx=m[node[now].cto[i]];
			if(sx==0)continue;//如果根本没有这个操作 
			if(!vis[sx]){
				if(bfs(sx)){
					return true;
				}
			}
		}
	}
	node[now].flag=-1;
	return false;
	
}
int main(){
	scanf("%d",&n);
	_forplus(i,1,n){
		scanf("%s",node[i].it);
		scanf("%d",&node[i].k);
		_forplus(j,1,node[i].k){
			scanf("%s",node[i].cto[j]);
		}
	//printf("first order: %d\n",i);
	}
	_forplus(i,1,n){
		m[node[i].it]=i;
	//printf("second order: %d\n",i);
	}
	_forplus(i,1,n){
	//printf("third order: %d\n",i);
		mem(vis,0);
		if(bfs(i)){
			cnt++;
		}
	}
	printf("%d",n-cnt);
	return 0;
}

结果:这个样例过了,下一个样例又超时了
难过,,,
cf上太长了无法完全显示。。。
和正确答案对拍没有错
看来就是被卡超时了

我看到CF上最快的代码
都没有用数据结构存储
他们的想法真是神奇

#include<bits/stdc++.h>
#define MAX_N 200000
#define ll long long
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text2.out","w",stdout);
#endif
using namespace std;
unordered_map<string,int>num;
int vis[500];
vector<int>g[405];
vector<int>vec;
int main(){
   //freopen("text.in","r",stdin);
   std::ios::sync_with_stdio(false);
   int n;
   string sta="::PROGRAM";
   string tmp;
   cin>>n;
   int cnt=1;
   for(int i=0;i<n;i++){
        int u=0;
        cin>>tmp;
        if(num[tmp]==0){
            num[tmp]=cnt++;
           // cout<<tmp<<"  "<<cnt-1<<endl;
            if(vis[num[tmp]]==0&&tmp.find(sta)!=tmp.npos){
                int t=tmp.find(sta);
               if(t+9==tmp.length())
               {vec.push_back(num[tmp]);
               vis[num[tmp]]=1;}
            }
        }
        u=num[tmp];
        int k;
        cin>>k;
        for(int j=0;j<k;j++){
            cin>>tmp;
            if(num[tmp]==0){
             num[tmp]=cnt++;
            //cout<<tmp<<"  "<<cnt-1<<endl;
             if(vis[num[tmp]]==0&&tmp.find(sta)!=tmp.npos){
               int t=tmp.find(sta);
               if(t+9==tmp.length())
               {vec.push_back(num[tmp]);
               vis[num[tmp]]=1;}
            }
         }
         g[num[tmp]].push_back(u);
        }
   }
   int c=vec.size();
   vector<int>nxt;
   while(vec.size()!=0){
        for(int i=0;i<vec.size();i++){
            int u=vec[i];
            for(int j=0;j<g[u].size();j++){
                int v=g[u][j];
                if(vis[v]==0){
                   // cout<<v<<endl;
                    nxt.push_back(v);
                    vis[v]=1;
                    c++;
                }
            }
        }
        vec=nxt;
        nxt.clear();
   }
   printf("%d\n",n-c);
   return 0;
}

但是看不太懂,,
找了一个水绿朋友的,还是这有共同语言www

知识点:智能指针

for(auto v: adj[u])
智能指针,遍历后面的数组元素

代码大意:
自底向上,往往高效
存储方法不同:
我是按原来的,一个下有哪些操作
他为了自底向上花了些心思,一个操作属于哪些子操作
只要这些子操作有效,那么该子操作上所有操作有效
再有一个vis数组防止重复
一个操作既然有效,如果他没dfs过,递归dfs,它上面的操作都有效

#include <bits/stdc++.h>
 
using namespace std;
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
#define st first
#define nd second
#define pb push_back
#define db(x) cerr << #x << " == " << x << endl
#define dbs(x) cerr << x << endl
#define _ << ", " <<
 
typedef long long ll;
typedef long double ld;
typedef pair<int,int>pii;
typedef pair<int,pii>piii;
typedef pair<ll,ll> pll;
typedef pair<ll,pll> plll;
typedef vector <int> vi;
typedef vector <vi> vii;
 
const ld EPS = 1e-9, PI = acos(-1.);
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f, MOD = 1e9+7;
const int N = 1e3+5;
 
map<string,int> num;
map<int,int> mark;
int n, vis[N], cnt, k, ans;
vi adj[N];
vector <string> names[N];
 
void add_vert(string y){
    string cmp = "PROGRAM", aux;
    int l = 0;
    for(int i=0; i < y.size();i++){
        if(y[i] == ':'){
            l++;
            continue;
        }
        if(l < 2) continue;
        aux += y[i];
    }
    if(aux == cmp) mark[num[y]] = 1;
}
 
void dfs(int u){
    ans++;
    vis[u] = 1;
    for(auto v: adj[u]){
        if(!vis[v]) dfs(v);
    } 
}
 
int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n;
    for(int i=1; i <= n; i++){
        string s;
        cin >> s;
        num[s] = i;
        add_vert(s);
        cin >> k;
        for(int j=0;j<k;j++){
            string x;
            cin >> x;
            names[i].pb(x);
        }
    }
    for(int i=1; i<=n; i++){
        for(int j=0;j<names[i].size();j++){
            adj[num[names[i][j]]].pb(i);
        }
    }
    for(int i=1;i<=n;i++){
        if(mark[i] and !vis[i]) dfs(i);
    }
    cout << n - ans << "\n";
    return 0;
}

我发现了是因为一个地方没有标flag超时
但是改过后在第53例wa了
数据很长的那种
很长时间都不觉得自己错了
就好挫败
以后多自底向上编,有条理
最后我花了5个小时看一个代码还是不知道自己哪错了
今天恰好EC final ,斌姐姐最后一次上场,我真希望他能实现自己的心愿夺金啊
我和他同甘共苦,不虚此行

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
//#include<iostream>
//#include<algorithm>
#include<string>
//#include<sstream>
//#include<vector>
#include<map>
//#include<set>
//#include<ctype.h>
//#include<stack>
//#include<queue>
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define _forplus(i,a,b) for( register int i=(a); i<=(b); i++)
#define _forsub(i,a,b) for( register int i=(a); i>=(b); i--)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define INF 0x3f3f3f3f
#define pi (acos(-1))
#define N 600
struct Node{
	int flag=0;//默认0 有效1 无效-1 减少重复 
	char it[N];//存操作 读入 
	int k;//子个数 读入 
	char cto[N][N];//子去处名称 读入 
}node[N];
map<string,int>m;//子去处记录 读完出 小心死循环超时 
//注意:char*作为指针,是不能判断字符串的,只能判断存放地址 
//并且string 可以兼容 char[] ,我只修改了这里   
int vis[N];//看每一个时防止死循环 
int n,cnt=0;
bool jd(char*str){
	char*p=strchr(str,':')+2;
	//printf("%s\n",p);
	if(strcmp(p,"PROGRAM")==0)return true;
	else return false;
}//判断该字符串是否能有效 
bool bfs(int now,int cnt){
	if(now==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("%s %d %d %d\n",node[i].it,node[i].k,node[i].flag,cnt);
		}
	vis[now]=1;
	if(node[now].flag==1){
		return true;
		if(now==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("f1:%s %d %d %d\n",node[i].it,node[i].k,node[i].flag,cnt);
		}
	}
	if(node[now].flag==-1){
		if(now==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("f2:%s %d %d %d\n",node[i].it,node[i].k,node[i].flag,cnt);
		}
		return false;
	}//flag减少重复 
	if(jd(node[now].it)){
		node[now].flag=1;
		if(now==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("second:%s %d %d %d\n",node[i].it,node[i].k,node[i].flag,cnt);
		}
		return true;
	}else{
		_forplus(i,1,node[now].k){
			//printf("in %d order: %d\n",now,i);
			int sx=m[node[now].cto[i]];
			if(sx==0)continue;//如果根本没有这个操作 
			if(!vis[sx]){
				if(bfs(sx,cnt+1)){
					node[now].flag=1;//记得找到子串后,递归回来也要标可以,避免重复!!
					//可由我们返回就是该字符串有效,flag也是这个用想到 
					if(now==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("third:%s %d %d %d\n",node[i].it,node[i].k,node[i].flag,cnt);
		}
					return true;
				}
			}
		}
	}
		if(now==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("last:%s %d %d %d\n",node[i].it,node[i].k,node[i].flag,cnt);
		}
	node[now].flag=-1;
	return false;
	
}
int main(){
	scanf("%d",&n);
	_forplus(i,1,n){
		scanf("%s",node[i].it);
		scanf("%d",&node[i].k);
		_forplus(j,1,node[i].k){
			scanf("%s",node[i].cto[j]);
		}
	//printf("first order: %d\n",i);
	}
	_forplus(i,1,n){
		m[node[i].it]=i;
	//printf("second order: %d\n",i);
	}
	if(n==400&&node[1].k==398){
			int i=200;
			printf("%s %d \n",node[i].it,node[i].k);
			_forplus(j,200,node[i].k){
				if(jd(node[i].cto[j]))
				printf("this:%s\n",node[i].cto[j]);
			}
		}
	_forplus(i,1,n){
	//printf("third order: %d\n",i);
	/*if(i==200)
			if(n==400&&node[1].k==398){
			int i=200;
			printf("%s %d %d\n",node[i].it,node[i].k,node[i].flag);
			_forplus(j,200,node[i].k){
				printf("%s %d\n",node[i].cto[j],node[m[node[i].cto[j]]].flag);
			}
		}*/
		mem(vis,0);
		if(bfs(i,1)){
			cnt++;
		}

		
	}
	printf("%d\n",n-cnt);
	/*
	int sum=0;
	_forplus(i,1,n){
		if(node[i].flag==1){
			sum++;
		}
	}
	printf("%d",n-sum);
	//由此,为flag就标错了
	*/
	//查看数据
	if(n==400&&node[1].k==398){
		/*_forplus(i,1,n){
			if(node[i].flag==-1){
				printf("%d ",i);
			}
		}*/
		/*int i=200;
		printf("%s %d\n",node[i].it,node[i].k);
		_forplus(j,1,node[i].k){
			printf("%s %d\n",node[i].cto[j],node[m[node[i].cto[j]]].flag);
		}*/
		/*char *st=node[199].it;
		int flag=0;
		_forplus(i,1,n){
			flag=0;
			_forplus(j,1,node[i].k){
				if(!strcmp(node[i].cto[j],st)){
					flag=1;
				}
			}
			if(!flag){
				printf("%d\n",i);
			}
		}*/
	} 
	return 0;
}

——————
晚上一想,睡前想到了。
沉心静气很重要,甘做冷板凳很重要
每一次的失败,都是为最后的成功前进
——崇桂书老师
我看了整整5个小时,都调不出错误啊,感动死我了
WA样例:

3
A::A 2
C::C B::PROGRAM
B::PROGRAM 0

C::C 1
A::A

由于我的vis是走过了一个就不dfs这个了,会有漏解的情况
应该是vis哪个就在dfs中不看哪个才好,
比如上面,漏了C::C
否决:等价的不能换
想法2:如果一来一回,就往后扫
问题:不止一来一回,可能有三重四重,难以回去找到是第几层的DFS

总之,自顶向下写程序就是没有自底向上写程序好啦,我算是见识了
就光从DJ记录父节点而不能记录子节点就知道,
在这种有路径的图结构中(这个题可以认为是个图结构看)
自底向上百般好有条理有唯一性
自顶向下没有唯一性有分支,而知道了叶子即这里的::PROGRAM节点就可以追溯父节点没有分支,就十分有条理
应该从结果开始,动态规划回来的
试图修正的代码如下:

/*
错误原因:
自顶向下的多重来回,vis了的skip了之后的可能性,会漏解错解 
如果要再重新vis时略过,要记录是哪一个循环的哪一号,太麻烦
如果要等到回到那一层再把这层也记上,也很麻烦
但是要记录要做的话,每次开一个数组记录也不是不行,反正最多400层 
但是由于什么都要看,超时了,dfs调用太多次了 

还是自底向上DP解决才好 
有根据,有方向,不超时 
错误样例: 
3
A::A 2
C::C B::PROGRAM
B::PROGRAM 0

C::C 1
A::A 
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
//#include<iostream>
//#include<algorithm>
#include<string>
//#include<sstream>
//#include<vector>
#include<map>
//#include<set>
//#include<ctype.h>
//#include<stack>
//#include<queue>
#ifdef LOCAL
FILE*FP=freopen("text.in","r",stdin);
//FILE*fp=freopen("text.out","w",stdout);
#endif
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define _forplus(i,a,b) for( register int i=(a); i<=(b); i++)
#define forplus(i,a,b) for( register int i=(a); i<(b); i++)
#define _forsub(i,a,b) for( register int i=(a); i>=(b); i--)
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define INF 0x3f3f3f3f
#define pi (acos(-1))
#define N 405
struct Node{
	int flag=0;//默认0 有效1 无效-1 减少重复 
	char it[N];//存操作 读入 
	int k;//子个数 读入 
	char cto[N][N];//子去处名称 读入 
}node[N];
map<string,int>m;//子去处记录 读完出 小心死循环超时 
//注意:char*作为指针,是不能判断字符串的,只能判断存放地址 
//并且string 可以兼容 char[] ,我只修改了这里   
//int vis[N];//看每一个时防止死循环 
int ct[N]={1};//判层计数,1 ,替换vis,功能更强大 
int n,cnt=0;
inline bool jd(char*str){
	char*p=strchr(str,':')+2;
	//printf("%s\n",p);
	if(strcmp(p,"PROGRAM")==0)return true;
	else return false;
}//判断该字符串是否能有效 
inline bool bfs(int now){
	if(node[now].flag==1){
		return true;
	}
	if(node[now].flag==-1){
		return false;
	}//flag减少重复 
	if(jd(node[now].it)){
		node[now].flag=1;
		return true;
	}else{
		//vis[now]=1;
		_forplus(i,ct[now],node[now].k){
			ct[now]=i+1;
			//printf("in %d order: %d\n",now,i);
			int sx=m[node[now].cto[i]];
			if(sx==0)continue;//如果根本没有这个操作 
			//if(!vis[sx]||ct[sx]!=1){
				if(bfs(sx)){
					node[now].flag=1;//记得找到子串后,递归回来也要标可以,避免重复!!
					//可由我们返回就是该字符串有效,flag也是这个用想到 
					return true;
				}
			//}
		}
	}
	node[now].flag=-1;
	return false;
	
}
int main(){
	scanf("%d",&n);
	_forplus(i,1,n){
		scanf("%s",node[i].it);
		scanf("%d",&node[i].k);
		_forplus(j,1,node[i].k){
			scanf("%s",node[i].cto[j]);
		}
	//printf("first order: %d\n",i);
	}
	_forplus(i,1,n){
		m[node[i].it]=i;
	//printf("second order: %d\n",i);
	}
	_forplus(i,1,n){
	//printf("third order: %d\n",i);
		//mem(vis,0);
		forplus(i,1,n){
			ct[i]=1;
		} 
		if(bfs(i)){
			cnt++;
		}
	}
	printf("%d",n-cnt);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值