题意:有个人吃了两天饭,第一天吃了n个菜,第二天吃了m个菜,然后给出一个矩阵,矩阵上第i行第j列对应着第1天的第i道菜和第二天的第j道菜的关系。
题解:这题,一眼看上去真的是和差分约束没啥区别,打了一发,T10,优化到了T13,实在无能为力,有大佬优化好的可以贴下评论。
附上代码:
#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
const int maxn=1e3+5;
const int maxm=1e3+5;
const int maxe=2*maxn*maxm;
char s[5];
struct edge{int v,w,next;}edges[maxe];;
int head[maxn+maxm],tot;
void init()
{
tot=1;
}
void add_edges(int u,int v,int w)
{
edges[tot].v=v;edges[tot].w=w;edges[tot].next=head[u];head[u]=tot++;
}
int n,m,dis[maxn+maxm],cnt[maxn+maxm];
bool vis[maxn+maxm];
int sta[10*(maxn+maxm)];
bool spfa()
{
int s=0,t=0;
sta[t++]=n+m+1;
dis[n+m+1]=1;cnt[n+m+1]=1;vis[n+m+1]=true;
while(s<t){
int u=sta[--t];
vis[u]=false;
for(int i=head[u];i;i=edges[i].next){
edge e=edges[i];
if(dis[e.v]<dis[u]+e.w){
dis[e.v]=dis[u]+e.w;
if(!vis[e.v]){
sta[t++]=e.v;
vis[e.v]=true;
if(++cnt[e.v]>n+m+1){
printf("No\n");
exit(0);
}
}
}
}
}
return true;
}
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%1s",s);
if(s[0]=='>')add_edges(j+n,i,1);
else if(s[0]=='<')add_edges(i,j+n,1);
else{
add_edges(j+n,i,0);add_edges(i,j+n,0);
}
}
}
for(int i=1;i<=n+m;i++)add_edges(n+m+1,i,0);
if(spfa()){
printf("Yes\n");
for(int i=1;i<=n;i++)printf("%d ",dis[i]);
printf("\n");
for(int i=1;i<=m;i++)printf("%d ",dis[i+n]);
printf("\n");
}else{
printf("No\n");
}
return 0;
}
这样不行,但是可以发现这个题是这个传送门的弱化版,可以将小于号从小到大连一条边,大于号也是转化为小于号,从小到达连一下,还可以将等于号转化成双向边,然后进行tarjan寻找环缩点,之后再特判一遍是否环上所有的点都相等,然后之后缩点后重新建图,这一个是一个标准的拓扑排序,直接在这个拓扑排序上进行dp即可。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+50;
char s[1050][1050];
int n,m;
struct edge{int v,next;}e1[maxn*maxn*2],e2[maxn*maxn*2];
int head1[maxn*2],head2[maxn*2],tot1,tot2;
int cnt,top,dfn[maxn*2],low[maxn*2],ltk[maxn*2],sta[maxn*2];
int du[maxn*2],h,t,que[maxn*2],dis[maxn*2];
void add1(int u,int v)
{
e1[++tot1].v=v;e1[tot1].next=head1[u];head1[u]=tot1;
}
void add2(int u,int v)
{
du[v]++;
e2[++tot2].v=v;e2[tot2].next=head2[u];head2[u]=tot2;
}
void dfs(int x)
{
dfn[x]=low[x]=++cnt;sta[++top]=x;
for(int i=head1[x];i;i=e1[i].next){
if(!dfn[e1[i].v]){
dfs(e1[i].v);
low[x]=min(low[x],low[e1[i].v]);
}else if(!ltk[e1[i].v]){
low[x]=min(low[x],dfn[e1[i].v]);
}
}
if(dfn[x]==low[x]){
ltk[0]++;
while(top){
ltk[sta[top]]=ltk[0];
if(sta[top--]==x)break;
}
}
}
void rebuild()
{
for(int i=1;i<=n+m;i++){
for(int j=head1[i];j;j=e1[j].next){
if(ltk[i]!=ltk[e1[j].v]){
add2(ltk[i],ltk[e1[j].v]);
}
}
}
}
void toposort()
{
for(int i=1;i<=ltk[0];i++){
if(!du[i]){
que[++t]=i;
dis[i]=1;
}
}
while(h<t){
int x=que[++h];
for(int i=head2[x];i;i=e2[i].next){
dis[e2[i].v]=dis[x]+1;
if(!--du[e2[i].v]){
que[++t]=e2[i].v;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>s[i][j];
if(s[i][j]=='='){
add1(i,j+n);add1(j+n,i);
}else if(s[i][j]=='>'){
add1(j+n,i);
}else{
add1(i,j+n);
}
}
}
for(int i=1;i<=n+m;i++){
if(!dfn[i]){
dfs(i);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]!='='){
if(ltk[i]==ltk[j+n]){
cout<<"No"<<endl;
return 0;
}
}
}
}
rebuild();toposort();
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++)cout<<dis[ltk[i]]<<" ";cout<<endl;
for(int i=1;i<=m;i++)cout<<dis[ltk[i+n]]<<" ";cout<<endl;
return 0;
}
之后再网上还看到一种就是,可以将相等的点处理到一个并查集中,也就是说先处理第一遍,把认为相等的点放到一个并查集中,然后进行特判下,之后将按照小于号,将一个个并查集连接起来,还得处理一番,讲多重边去掉,然后如果是答案的话一定是一个有向无环图,然后在上面进行拓扑排序和dp,并带着处理一番环即可。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
vector<int>e[maxn*2];
char s[maxn][maxn];
int n,m;
int d[maxn*2],in[maxn*2],f[maxn*2];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void merge(int a,int b)
{
int x=find(a),y=find(b);
if(x!=y)f[y]=x;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n+m;i++)f[i]=i;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>s[i][j];
if(s[i][j]=='=')merge(find(i),find(j+n));
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]!='='){
if(find(i)==find(j+n)){
cout<<"No"<<endl;
return 0;
}
if(s[i][j]=='>'){
e[find(j+n)].push_back(find(i));
}else if(s[i][j]=='<'){
e[find(i)].push_back(find(j+n));
}
}
}
}
for(int i=1;i<=n+m;i++)sort(e[i].begin(),e[i].end());
for(int i=1;i<=n+m;i++)e[i].erase(unique(e[i].begin(),e[i].end()),e[i].end());
for(int i=1;i<=n+m;i++){
for(int j=0;j<e[i].size();j++)in[e[i][j]]++;
}
queue<int>q;
for(int i=1;i<=n+m;i++){
if(!in[i]){
q.push(i),d[i]=1;
}
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<e[u].size();i++){
int nex=e[u][i];
in[nex]--;
d[nex]=d[u]+1;
if(in[nex]==0)q.push(nex);
}
}
if(*max_element(in+1,in+n+m+1)>0){
cout<<"No"<<endl;
return 0;
}
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++)cout<<d[find(i)]<<" ";cout<<endl;
for(int i=1;i<=m;i++)cout<<d[find(i+n)]<<" ";cout<<endl;
return 0;
}