1、UVA1423
题意:给出一个矩阵表示一个序列任意区间内区间和的正负,构造一个序列满足这个矩阵。
题解:首先看到给了区间和,第一个想法就应该是转化到前缀和的差,转化之后发现序列具有一种显见的偏序关系,然后这种偏序关系轻易地就想到了转换为图上问题去解决,这种思想在差分约束中也比较常见,以后有时间详细总结。然后考虑到了topo序,如果我们每次要求topo序为i的前缀和与比它topo序大的前缀和中最小的前缀和的差距不大于1,最大的前缀和数为10,最后求出来的数就一定在[-10,10]里面。我们把pre[i]向比它小的pre[j]建有向边,然后topo序在后的肯定不大于topo序在前的,这是显然的, 然后假设我们已经把topo序前k大的求出来了,求到第k+1大的时候,把它设成pre[k]-1并不会导致无解,如果把最大的pre设成10,找到一个topo序更靠后的,把它的值设成9,下一个设成8……就是这种感觉。我们就可以把topo序第k大的pre设成第k-1大的pre值-1,因为是topo排序,不会影响到pre之间的大小关系。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; int T,n; vector<int> G[15]; int deg[15]; char S[305]; int ans[15]; void add(int from,int to){ G[from].push_back(to); deg[to]++; } void init(){ memset(deg,0,sizeof(deg)); scanf("%d\n",&n); for (int i=0;i<=n;i++) G[i].clear(); int tot=0; scanf("%s\n",S+1); for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) if (S[++tot]=='+') add(j,i-1); else if (S[tot]=='-') add(i-1,j); } void Sort(){ queue<int> Q; for(int i=0;i<=n;i++) if(deg[i]==0) Q.push(i); int now=10; while(!Q.empty()){ int u=Q.front();Q.pop(); ans[u]=now; for(int i=0;i<G[u].size();i++){ int v=G[u][i]; deg[v]--; if(deg[v]==0){ now--; Q.push(v); } } } } int main(){ scanf("%d",&T); for (int i=1;i<=T;i++){ init(); Sort(); for (int i=1;i<n;i++) printf("%d ",ans[i]-ans[i-1]); printf("%d\n",ans[n]-ans[n-1]); } return 0; }
题意:问画出一个图最少需要多少笔。
题解:欧拉回路模板题,先利用并查集统计出图里有多少个连通分支,然后统计一下各个连通分支里欧拉回路或者欧拉道路的个数。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; const int maxn = 100010; int f[maxn],d[maxn],odd[maxn]; bool vis[maxn]; int find(int x){ if(x!=f[x]) f[x] = find(f[x]); return f[x]; } vector<int>p; int main(){ int n,m,u,v; while(~scanf("%d%d",&n,&m)){ for(int i=0;i<maxn;i++){ f[i]=i; odd[i]=d[i]=0; vis[i]=0; } p.clear(); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); d[u]++; d[v]++; u=find(u); v=find(v); if(u!=v) f[v]=u; } for(int i=1;i<=n;++i){ u=find(i); if(!vis[u]){ vis[u]=1; p.push_back(u); } if(d[i]&1) odd[u]++; } int ret=0; for(int i=0;i<p.size();i++){ if(!d[p[i]]) continue; if(odd[p[i]]==0) ret++; else ret+=odd[p[i]]/2; } printf("%d\n",ret); } return 0; }
题意:2*n个孩子,每个孩子最多有n-1个仇人,有仇的两个孩子不能相邻。求安排座位。
题解:哈密顿回路模板,根据初中数学可以看出来必定是有解的。然后套模板求个可行遍历就可以了。关于如何求哈密顿回路之前的博客里讲过。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; const int maxn=420; int n,m; bool mp[maxn][maxn]; int S,T,cnt,ans[maxn]; bool vis[maxn]; void reverse(int l,int r){ while (l<r){ swap(ans[l],ans[r]); l++,r--; } } void expend(){ for (;;){ bool flag=0; for (int i=1;i<=n;i++){ if (!vis[i] && mp[T][i]){ ans[cnt++]=i; T=i; vis[i]=1; flag=1; break; } } if (!flag) break; } } void hamiltun(){ memset(vis,0,sizeof(vis)); S=1; for (T=2;T<=n;T++) if (mp[S][T]) break; cnt=2; ans[0]=S, ans[1]=T; vis[S]=1, vis[T]=1; while (1) { expend(); reverse(0,cnt-1); swap(S,T); expend(); int mid=0; if (!mp[S][T]){ for (int i=1;i<cnt-2;i++){ if (mp[ans[i]][T] && mp[ans[i+1]][S]){ mid=i+1; break; } } reverse(mid,cnt-1); T=ans[cnt-1]; } if (cnt==n) break; for (int i=1;i<=n;i++){ if (!vis[i]){ int j; for (j=1;j<cnt-1;j++) if (mp[ans[j]][i]) break; if (mp[ans[j]][i]){ T=i; mid=j; break; } } } S=ans[mid-1]; reverse(0,mid-1); reverse(mid,cnt-1); ans[cnt++]=T; vis[T]=1; } } int main(){ int u,v; while (~scanf("%d%d",&n,&m)){ if (n==0 && m==0) break; n<<=1; memset(mp,1,sizeof(mp)); for (int i=1;i<=n;i++) mp[i][i]=0; for (int i=1;i<=m;i++){ scanf("%d%d",&u,&v); mp[u][v]=0; mp[v][u]=0; } hamiltun(); printf("%d",ans[0]); for (int i=1;i<cnt;i++) printf(" %d",ans[i]); puts(""); } return 0; }
题意:……题意好长懒得打了……题号都有啦网上去找嘛!
题解:这个题型我是见得多啦,看上去就是个搜嘛,但是实现起来真是恶心,也可能是我好久没写搜索了。首先先不管其他条件搜一下能不能到达终点。如果可以的话,题目要求的这个监控的条件,使得这个题要用到优先队列,因为每个格子面对每个方向都有三个选择,用时都各不相同,所以就把这些情况对应的不同时间都记录在结构体里然后丢进优先队列。第二个难点是如何记录T秒时监控的方向?结构体里多维护一个time用来%4就可以啦。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; const int Max=505; char str[Max][Max]; char cam[5]="NESW"; int dir[5][2]={-1,0,1,0,0,-1,0,1,0,0}; struct p{ int pos,time; bool operator < (const p& a)const{ return time>a.time; } bool operator == (const p& a) const{ return pos==a.pos; } }; p s,e,tmpp,np; int n,t,ans,c,nxt; bool vis[4][Max*Max]; bool fnd(int cur,int i,int& nxt) { int cx=cur/n; int cy=cur%n; int nx=cx+dir[i][0]; int ny=cy+dir[i][1]; if (nx>=0 && nx<n && ny>=0 && ny<n && str[nx][ny]!='#'){ nxt=nx*n+ny; return 1; } return 0; } bool can(){ bool flag[255025]={0}; queue<int>q; q.push(s.pos); while (q.empty()!=1){ int tmp=q.front(); q.pop(); for (int i=0;i<4;i++) if (fnd(tmp,i,nxt)==1 && flag[nxt]==0){ flag[nxt]=1; if (nxt==e.pos) return 1; q.push(nxt); } } return 0; } int isc(int xx,int yy){ for (int i=0;i<4;i++) if (str[xx][yy]==cam[i]) return i; return -1; } bool hasc(int pos,int time){ int x=pos/n; int y=pos%n; int d; if (x-1>=0) if ((d=isc(x-1,y))!=-1 && cam[(d+time%4)%4]=='S') return 1; if (x+1<n) if ((d=isc(x+1,y))!=-1 && cam[(d+time%4)%4]=='N') return 1; if (y-1>=0) if ((d=isc(x,y-1))!=-1 && cam[(d+time%4)%4]=='E') return 1; if (y+1<n) if ((d=isc(x,y+1))!=-1 && cam[(d+time%4)%4]=='W') return 1; if (isc(x,y)!=-1) return 1; return 0; } int main(){ int T; scanf("%d",&T); for (int cas=1;cas<=T;cas++){ scanf("%d",&n);getchar(); for (int i=0;i<n;i++){ for (int j=0;j<n;j++){ cin>>str[i][j]; if (str[i][j]=='M'){ s.pos=i*n+j; s.time=0; } if (str[i][j]=='T'){ e.pos=i*n+j; } } } if (can()){ memset(vis,0,sizeof(vis)); priority_queue<p> mq; mq.push(s); vis[s.time%4][s.pos]=1; while (1){ tmpp=mq.top(); mq.pop(); if (tmpp.pos==e.pos){ ans=tmpp.time; break; } for (int i=0;i<5;i++) if (fnd(tmpp.pos,i,np.pos)==true){ if (i<4 && (hasc(tmpp.pos,tmpp.time) || hasc(np.pos,tmpp.time))){ np.time=tmpp.time+3; } else np.time=tmpp.time+1; if (vis[np.time%4][np.pos]==0){ vis[np.time%4][np.pos]=1; mq.push(np); } } } } else ans=-1; printf("Case #%d: %d\n",cas,ans); } return 0; }
题意:给出一个无向图问你能不能把图里原来边长为1的边接一接变成边长为2连接三个点的边并覆盖全图。
题解:这个题看错题了,看了好久都没理解题意。以为是把原来的边剖开然后找点接上,怎么想都想不明白。其实就是对点进行dfs,找到一个点发现这个点有落单边,则把这个落单边连的终点和搜的这个点和上条边连的点输出,有时候这个点是一个类似于中心的点,这样就把其他连着它的点都丢进这个点专属的队列里,然后输出就行。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; const int N=100010; int from[200020],to[200020]; int edge[200020],head[200020]; int n,m,cnt; bool vis[200020]; void add(int x,int y){ from[cnt]=x; to[cnt]=y; edge[cnt]=head[x]; head[x]=cnt++; from[cnt]=y; to[cnt]=x; edge[cnt]=head[y]; head[y]=cnt++; } int dfs(int x,int f){ queue<int> q; for (int i=head[x];i!=-1;i=edge[i]){ int nx=to[i]; if (vis[i] || nx==f) continue; vis[i]=vis[i^1]=1; int tmp=dfs(nx,x); if (tmp) printf("%d %d %d\n",x,nx,tmp); else { q.push(nx); } } while (q.size()>=2){ int a=q.front(); q.pop(); int b=q.front(); q.pop(); printf("%d %d %d\n",a,x,b); } while (!q.empty()){ int a=q.front(); q.pop(); return a; } return 0; } int main(){ while (scanf("%d%d",&n,&m)!=EOF){ cnt=0; memset(vis,0,sizeof(vis)); memset(head,-1,sizeof(head)); int x,y; for (int i=1;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y); } if (m&1) { puts("No solution"); continue; } dfs(1,-1); } return 0; }
1、需要注意一下其他问题到图论问题的转化上,比如差分约束和第一题这种带偏序关系的题目。
2、注意代码实现的细节,第四题为了使用结构体的优先队列,重载了运算符,却没有传进去引用,容易GG,而且最好不要这么写,优先队列有三个参数,可以自己写个仿函数传进去。同样是第四题,没有注意函数定义的顺序,起初定义的find函数在引用函数的下面,所以编译器自动给我用了类里的find函数,导致莫名CE。
3、注意读题。