题意:有n个池塘和m个管道;每个池塘的价值是v, 现在由于资金问题要删除池塘;但是删除的池塘必须是最多只连接一个管道,否则会爆炸;管子会因为池塘的删除而消失
求最后相连的池塘有奇数个的价值总和是多少
解析:
建立无向图
先用拓扑排序删除入度为1的所以池塘并标记,然后再用深搜判断是否是奇数连通
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
int w[MAXN],in[MAXN];
int to[MAXN<<2],next1[MAXN<<2],head[MAXN<<2];
int tot,n,m;
int vis[MAXN];
void init()
{
tot=0;
for(int i=0;i<MAXN;i++)
in[i]=vis[i]=head[i]=next1[i]=0;
}
void add(int u,int v)
{
to[++tot]=v;
next1[tot]=head[u];
head[u]=tot;
}
void topo()
{
queue<int> que;
for(int i=1;i<=n;i++)
if(in[i]==1)
que.push(i),vis[i]=1;
while(que.size()!=0)
{
int u=que.front();
in[u]--;
que.pop();
for(int i=head[u];i;i=next1[i])
{
int v=to[i];
if(vis[v]==1)
continue;
in[v]--;
if(in[v]==1)
que.push(v),vis[v]=1;
}
}
}
ll ans=0,num=0,sum=0;
void dfs(int x)
{
num++;
sum+=w[x];
vis[x]=1;
for(int i=head[x];i;i=next1[i])
{
int v=to[i];
if(vis[v]==1)
continue;
dfs(v);
}
}
int main()
{
int t,a,b;
scanf("%d",&t);
while(t--)
{
init();//ÖØÖÃ
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
in[a]++;
in[b]++;
}
topo();
ans=0;
for(int i=1;i<=n;i++)
{
if(in[i]>=2&&vis[i]==0)
{
num=0;//ÅжÏÆæż
sum=0;//ÀÛÊý
dfs(i);
ans+=sum*(num%2);
}
}
printf("%lld\n",ans);
}
return 0;
}
D. Gourmet choice
题意:
给两个数组,长度分别为n,m.给两个数组的关系
构造出最小的两个数组,如果无法构造,输出NO,否则YES,输出两个数组
解析:
并查集(缩点)+拓扑排序
首先要判断是否有环,有环NO
对与"=",我们把它合成一个点,用并查集
用拓扑排序判断是否有环和构造数组
ac:
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define IO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define ll long long
#define MAXN 5005
using namespace std;
char str[1010][1010]={0};
int f[MAXN];
int in[MAXN]={0};
vector<int> mp[MAXN];
int reward[MAXN]={0};
int n,m;
void init(){
rep(i,1,MAXN)
f[i]=i;
}
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
void unite(ll a,ll b){
ll x=find(a),y=find(b);f[x]=y;
}
void topo()
{
queue<int> que;
for(int i=1;i<=n+m;i++)
if(in[i]==0)
{
reward[i]=1;
que.push(i);
}
int count=0;
while(!que.empty())
{
int top=que.front();
que.pop();
count++;
for(int i=0;i<mp[top].size();i++)
{
in[mp[top][i]]--;
if(in[mp[top][i]]==0)
{
que.push(mp[top][i]);
reward[mp[top][i]]=max(reward[mp[top][i]],reward[top]+1);
}
}
}
if(count!=n+m)//判环
cout<<"NO"<<endl;
else{
cout<<"YES"<<endl;
for(int i=1;i<=n;i++)
cout<<reward[find(i)]<<(n-i?' ':'\n');
for(int i=1;i<=m;i++)
cout<<reward[find(n+i)]<<(n-i?' ':'\n');
}
}
int main()
{
init();
scanf("%d%d",&n,&m);
rep(i,1,n)
scanf("%s",str[i]+1);
rep(i,1,n)
rep(j,1,m)
if(str[i][j]=='=')
unite(i,j+n);//合点
rep(i,1,n)
rep(j,1,m)
{
if(str[i][j]=='=')
continue;
if(str[i][j]=='>')
{
mp[find(n+j)].push_back(find(i));
in[find(i)]++;
}
else {
mp[find(i)].push_back(find(n+j));
in[find(n+j)]++;
}
}
topo();
return 0;
}
https://ac.nowcoder.com/acm/contest/885/H
题意:
有一个字符串,把所以字符的关系给你,让你输出这个字符,如果不存在这个字符,输出-1
解析:
这里每种字母最多10000个,我们直接把不同的字母构成分别村好,每种字母前后差10000
然后就是常规操作
ac:
#include<bits/stdc++.h>
#define MAXN 300005
using namespace std;
char s[MAXN]={0};
int in[MAXN]={0};
int vis[MAXN]={0};
int c[MAXN]={0};
int num[30];//26个字母的数目
vector<int> vc[MAXN];//记录边
int ans[MAXN]={0};
void topo(int n)
{
priority_queue<int> que;
int cnt=0;
for(int i=0;i<26;i++)
{
for(int j=1;j<=num[i];j++)
if(in[i*10000+j]==0)
que.push(i*10000+j),vis[i*10000+j]=1;
cnt+=num[i];
}
int num=0;
while(que.size())
{
int u=que.top();
ans[++num]=u;
que.pop();
for(int i=0;i<vc[u].size();i++)
{
int v=vc[u][i];
in[v]--;
if(in[v]==0&&vis[v]==0)
que.push(v),vis[v]=1;
}
}
if(num!=n||cnt!=n||cnt!=num)
printf("-1\n");
else{
for(int i=1;i<=n;i++)
printf("%c",ans[i]/10000+'a');
printf("\n");
}
}
int main()
{
char pp[5];
int n,m,len;
scanf("%d%d",&n,&m);
for(int i=1;i<=m*(m-1)/2;i++)
{
scanf("%s",pp+1);
int a=pp[1]-'a';//第一个点
int b=pp[2]-'a';//第二个点
scanf("%d",&len);
if(len==0)
continue;
scanf("%s",s+1);
int acnt=0,bcnt=0;
if(s[1]-'a'==a)
c[1]=10000*(s[1]-'a')+(++acnt);
else c[1]=10000*(s[1]-'a')+(++bcnt);
for(int i=2;i<=len;i++)
{
if(s[i]-'a'==a)
c[i]=10000*(s[i]-'a')+(++acnt);
else
c[i]=10000*(s[i]-'a')+(++bcnt);
in[c[i]]++;//入度
vc[c[i-1]].push_back(c[i]);
}
num[a]=acnt;//记录数目
num[b]=bcnt;
}
topo(n);
return 0;
}
https://codeforces.com/contest/1230/problem/D
题意:
有n个人,每个人有一个能力可能有(0-59种能力),b[i]是能力的权值
a[i]来描述能力,将a[i]转化为二进制,如果第i位为1,则他会第i个技能
如果一个人的能力比另外一个人强,只要存在一个技能,他会,另一个人不会,两个人可以互相都比对方强
你可以选择一些元素组成一个集合,该集合中,不存在一个人比其他任何人都强
解析:
法1:O(n^2)复杂度
选择的若干人中,每个人都至少能战胜别人一次,和被战胜一次
建图跑拓扑排序,拓扑排序删边,剩下的图就是答案
这题可以是稠密图,用链式前向星会爆内存,我这里直接用二维数组,时间换空间,用vector也行
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 7005
using namespace std;
ll a[MAXN],b[MAXN];
int mm[MAXN][MAXN]={0};
int n;
int in[MAXN],vis[MAXN]={0};
void add(int u,int v)
{
in[v]++;
mm[u][v]=1;
}
void topo()
{
queue<int> que;
for(int i=1;i<=n;i++)
if(in[i]==0)
que.push(i);
while(que.size())
{
int x=que.front();
vis[x]=1;
que.pop();
for(int i=1;i<=n;i++)
{
if(!mm[x][i])
continue;
in[i]--;
if(in[i]==0){
que.push(i);
}
}
}
}
int main()
{
scanf("%d",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=1;i<=n;i++)
scanf("%d",&b[i]);
for(ll i=1;i<=n;i++)
{
for(ll j=1;j<=n;j++)
{
if(i==j)
continue;
ll aa=a[i];
ll bb=a[j];
if((aa|bb)==aa){
add(i,j);
}
}
}
topo();
ll ans=0;
for(int i=1;i<=n;i++)
{
if(vis[i]==0)
ans+=b[i];
}
printf("%lld\n",ans);
return 0;
}
法2:
解析:O(n)复杂度
根据性质:
在一个集合中,不能存在一个技能只有一个人会的情况
=> 首先我们选择一些元素,这些元素的a[i]相同,我们选择一些a[i]相同的元素加入集合,如果所以a[i]都唯一,呢么集合为空
-> 我们选择这些包含相同a[i]的,然后选择一些a[i]包含在呢些集合中的
集合中有:11100*2,000111*2,
可以选择10100,000110,会的技能被任一元素的a[i]包含,不能选择001100,不能被单独一个包含
用|运算可以快速取得结果, a[i]|v[i]==v[i],a[i]被v[i]包含,a[i]会的v[i]都会
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 10005
using namespace std;
struct node
{
ll a,b;
friend bool operator<(node x,node y)
{
return x.a<y.a;
}
}ee[MAXN];
ll vis[MAXN];
set<ll> st;
int main()
{
ll n;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&ee[i].a);
for(ll i=1;i<=n;i++)
scanf("%lld",&ee[i].b);
if(n==1)
{
printf("0\n");
return 0;
}
sort(ee+1,ee+n+1);
for(ll i=1;i<=n;i++)
{
if(ee[i].a==ee[i+1].a&&i+1<=n)
st.insert(ee[i].a);
}
set<ll>::iterator it;
ll ans=0;
for(it=st.begin();it!=st.end();it++)
{
ll v=*it;
for(int i=1;i<=n;i++)
{
if((v|ee[i].a)==v&&vis[i]==0)
{
ans+=ee[i].b;
vis[i]=1;
}
}
}
printf("%lld\n",ans);
return 0;
}