图论专题 菜肴制作 矩阵游戏 魔法森林 飞行路线
二维spfa,不过需要优化,用优先队列比较快,手打普通队列应该也可以
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 10005 using namespace std; int n,m,K; int s,t; struct edge { int to,ne,w; }b[maxn*10]; int k=0,head[maxn]; int dis[maxn][13]; struct node { int id,ti; bool operator <(const node& a) const { return dis[id][ti]>dis[a.id][a.ti]; } }tmp,now; bool vis[maxn][13]; priority_queue<node> q; void add(int u,int v,int w) { k++; b[k].to=v;b[k].ne=head[u];b[k].w=w;head[u]=k; } void spfa(int x) { memset(dis,0xf,sizeof(dis)); dis[x][0]=0; tmp.id=x;tmp.ti=0; q.push(tmp); vis[x][0]=1; while(!q.empty()){ now=q.top();q.pop(); int z=now.id; int t=now.ti; vis[z][t]=0; for(int i=head[z];i!=-1;i=b[i].ne){ if(dis[b[i].to][t+1]>dis[z][t]&&t+1<=K){ dis[b[i].to][t+1]=dis[z][t]; if(vis[b[i].to][t+1]==0){ vis[b[i].to][t+1]=1; tmp.id=b[i].to; tmp.ti=t+1; q.push(tmp); } } if(dis[b[i].to][t]>dis[z][t]+b[i].w){ dis[b[i].to][t]=dis[z][t]+b[i].w; if(vis[b[i].to][t]== 0){ vis[b[i].to][t]=1; tmp.id=b[i].to;tmp.ti=t; q.push(tmp); } } } } return ; } int main() { //freopen("in.txt","r",stdin); memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&m,&K); scanf("%d%d",&s,&t); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } spfa(s); int ans=dis[t][0]; for(int i=1;i<=K;i++) ans=min(ans,dis[t][i]); printf("%d",ans); //while(1); return 0; }
NOI魔法森林
正解很麻烦,可以维护动态spfa,从小到大枚举a,动态插入ai值小于a的边,根据新插入的点更新dis,如果dis[n]被更新,那么经过路线ai上的最大值一定是a,然后每次动态维护后ans=min(ans,dis[n]+a);
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<algorithm> #define maxn 100005 using namespace std; int n,m; int dis[maxn]; struct edge2 { int fr,to,a,b; }s[maxn*2]; int kk=0; struct edge { int to,ne,w; }b[maxn*2]; int k=0,head[maxn]; bool vis[maxn]; struct node { int id; bool operator <(const node &a) const { return dis[id]>dis[a.id]; } }tmp,now; priority_queue<node> q; void add(int u,int v,int w) { k++; b[k].to=v;b[k].ne=head[u];b[k].w=w;head[u]=k; } void add2(int u,int v,int a,int b) { kk++; s[kk].fr=u;s[kk].to=v;s[kk].a=a;s[kk].b=b; } inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x; } int cmp(const edge2 &q,const edge2 &w) { return q.a<w.a; } void spfa() { int id; while(!q.empty()){ now=q.top();q.pop(); id=now.id; vis[id]=0; for(int i=head[id];i!=-1;i=b[i].ne) if(dis[b[i].to]>max(dis[id],b[i].w)){ dis[b[i].to]=max(dis[id],b[i].w); if(!vis[b[i].to]){ vis[b[i].to]=1; tmp.id=b[i].to; q.push(tmp); } } } } int main() { //freopen("magicalforest20.in","r",stdin); //freopen("out.txt","w",stdout); memset(head,-1,sizeof(head)); memset(dis,0x7f,sizeof(dis)); scanf("%d%d",&n,&m); int x,y,ai,bi; int op; int ans=0x7fffffff; for(int i=1;i<=m;i++){ scanf("%d%d%d%d",&x,&y,&ai,&bi); add2(x,y,ai,bi); } sort(s+1,s+m+1,cmp); dis[1]=0;vis[1]=1; tmp.id=1; q.push(tmp); for(int i=1;i<=m;i++){ op=s[i].a; int j=i; for(j=i;j<=m;j++) if(s[j].a==op){ add(s[j].fr,s[j].to,s[j].b); add(s[j].to,s[j].fr,s[j].b); if(!vis[s[j].fr]){ vis[s[j].fr]=1; tmp.id=s[j].fr;q.push(tmp); } if(!vis[s[j].to]){ vis[s[j].to]=1; tmp.id=s[j].to;q.push(tmp); } } else break; spfa(); ans=min(ans,dis[n]+op); i=j-1; } if(ans>1000000000) printf("-1\n"); else printf("%d\n",ans); return 0; }ZJOI矩阵游戏
一开始以为是个大爆搜,交上去就对了一个点,这大概是那30%的点,正解是二分图,每一个黑点占一行一列,二分图左边是行,右边是列,连边为黑点,如果最大匹配可以覆盖所有行列,那么一定存在一种解
#include<iostream> #include<cstdio> #include<cstring> #define maxn 405 using namespace std; int T,n; struct edge { int to,ne; }b[maxn*maxn]; int k=0,head[maxn],lin[maxn]; bool vis[maxn]; void add(int u,int v) { k++; b[k].to=v;b[k].ne=head[u];head[u]=k; } bool dfs(int x) { for(int i=head[x];i!=-1;i=b[i].ne){ int to=b[i].to; if(!vis[to]){ vis[to]=1; if(!lin[to]||dfs(lin[to])){ lin[to]=x; return 1; } } } return 0; } void init() { memset(head,-1,sizeof(head)); memset(lin,0,sizeof(lin)); memset(b,0,sizeof(b)); } int main() { scanf("%d",&T); for(int h=1;h<=T;h++){ init(); scanf("%d",&n); int x; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ scanf("%d",&x); if(x==1) add(i,j); } int ans=0; for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); if(dfs(i)) ans++; } if(ans==n) printf("Yes\n"); else printf("No\n"); } return 0; }HNOI菜肴制作
拓扑排序,如果正这搜,每次贪心的选择最小的编号,可能会忽略后面没有搜到的中更小的,而如果每次都搜最大的,可能忽略更大的,而后面更大的那个数如果没被搜到,因为更大数是一个更小数的前提,优先级比较高,所以在后面,这样搜就能保证每次都能搜到可行的最大数,然后倒着输出答案
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 1000005 using namespace std; int T,n,m; struct edge { int to,ne; }b[maxn]; int k=0,head[maxn]; int in[maxn],ans[maxn]; priority_queue<int,vector<int>,less<int> > q; inline int read() { int x=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();} return x; } inline void init() { memset(head,-1,sizeof(head)); memset(b,0,sizeof(b)); memset(in,0,sizeof(in)); memset(ans,0,sizeof(ans)); } inline void add(int u,int v) { k++; b[k].to=v;b[k].ne=head[u];head[u]=k; } int main() { T=read(); for(int h=1;h<=T;h++){ init(); n=read();m=read(); int x,y,num=0,js=0; for(int i=1;i<=m;i++){ x=read();y=read(); add(y,x);in[x]++; } for(int i=1;i<=n;i++)if(in[i]==0) {q.push(i);num++;} while(!q.empty()){ int z=q.top();q.pop(); ans[++js]=z; for(int i=head[z];i!=-1;i=b[i].ne) { in[b[i].to]--; if(in[b[i].to]==0){ q.push(b[i].to); num++; } } } if(num<n) printf("Impossible!\n"); else{ for(int i=n;i>=1;i--) printf("%d ",ans[i]); printf("\n"); } } return 0; }