来源题解目录
比赛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;
}