有 ()个提案和 ()个议员。每个议员可以对 ()个提案投票,作出 通过 或者 否决 的决定。现在你需要对每个议案的最终结果作出判断,使得每个议员的决定都有超过半数以上和最终结果相吻合。
首先要注意 的范围非常小,我们可以分情况讨论:
,那么这个议员的决定必须是最后结果,才能满足 使得每个议员的决定都有超过半数以上和最终结果相吻合。
,此时需要注意 超过半数以上 的含义,在原题的 pdf 文档中的英文描述是 more than half,这意味着符合的数目要比一半多,不能等于一半。 的时候的一半是 ,比一半多意味着两个决定都要与最终吻合。
,按照上述思路,需要有至少两个决定与最终结果吻合。也就是说如果有一个决定不吻合了,那么剩下的两个决定都必须吻合。
,一半是 ,超过一半,也就是 ,需要有至少 个决定与最终吻合,一个决定不吻合了剩下三个就都需要吻合。
这种如果一个决定不吻合,剩下两个/三个都必须吻合,启示我们可以使用 来解决。
下面规定 表示一个提案, 表示对这个提案做出的决定。点 表示的是提案为否定,;点 表示的是提案为通过,。
对于提案最终必须是某种情况,我们可以写个 函数:提案 最后必须是 情况。
cpp1234567inline void must(int e,char a){
if(a=='y'){
add(e,e+n);
}else{
add(e+n,e);
}
}
想让一个点必须是 ,可以让这个点的 向这个点的 连一条边。另一情况同理。
对于一个提案的决定与最终不符的话,其他决定必须相符的情况,我们可以写个 函数(具体含义看代码)。
cpp123456789inline void op(int e1,char a1,int e2,char a2){
if(a1=='y'){
if(a2=='y') add(e1,e2+n);
else add(e1,e2);
}else{
if(a2=='y') add(e1+n,e2+n);
else add(e1+n,e2);
}
}
(定义这两个函数的目的是为了之后的建边的代码好写)
然后建边部分:
cpp12345678910111213141516171819202122232425262728293031323334353637383940414243for(int i=1;i<=m;i++){
int k,e1,e2,e3,e4;
char a1,a2,a3,a4;
cin>>k;
if(k==1){
cin>>e1>>a1;
must(e1,a1);
}
if(k==2){
cin>>e1>>a1>>e2>>a2;
must(e1,a1);
must(e2,a2);
}
if(k==3){
cin>>e1>>a1>>e2>>a2>>e3>>a3;
op(e1,a1,e2,a2);
op(e1,a1,e3,a3);
op(e2,a2,e1,a1);
op(e2,a2,e3,a3);
op(e3,a3,e2,a2);
op(e3,a3,e1,a1);
}
if(k==4){
cin>>e1>>a1>>e2>>a2>>e3>>a3>>e4>>a4;
op(e1,a1,e2,a2);
op(e1,a1,e3,a3);
op(e1,a1,e4,a4);
op(e2,a2,e1,a1);
op(e2,a2,e3,a3);
op(e2,a2,e4,a4);
op(e3,a3,e2,a2);
op(e3,a3,e1,a1);
op(e3,a3,e4,a4);
op(e4,a4,e2,a2);
op(e4,a4,e1,a1);
op(e4,a4,e3,a3);
}
}
建好边之后就是常规的 解法:
:
cpp12345678910111213141516171819202122232425262728293031323334353637383940int n;
stack<int> s;
int low[maxn],dfn[maxn];
int _time;
bool lock[maxn];
int id[maxn];
int tot;
void dfs(int x);
void tarjan(){
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
_time=0;
memset(lock,0,sizeof(lock));
memset(id,0,sizeof(id));
tot=0;
for(int i=1;i<=n*2;i++){
if(!dfn[i]) dfs(i);
}
}
void dfs(int x){
low[x]=dfn[x]=++_time;
s.push(x);
lock[x]=true;
for(int i=head[x];i;i=edge[i].next){
int to=edge[i].to;
if(!dfn[to]){
dfs(to);
low[x]=min(low[x],low[to]);
}else if(lock[to]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x]){
tot++;
while(true){
int k=s.top();s.pop();
id[k]=tot;
lock[k]=false;
if(k==x) break;
}
}
}
这里在 dfs 之前把 , 等变量都重置了,因为我们不止会用一次 ,这个在后面会说。
然后单独写一个函数判断是否有解:
cpp12345678inline bool check(){
for(int i=1;i<=n;i++){
if(id[i]==id[i+n]){
return false;
}
}
return true;
}
然后我们将最后的可能的一种答案存放在 数组中,注意数组是 int
类型的,这个后面也会说。
cpp1234for(int i=1;i<=n;i++){
if(id[i+n]<id[i]) ans[i]=true;
else ans[i]=0;
}
然后题目和一般的 不同,他不让你求一种可能结果,而是对于一些可以通过,也可以否定的提案输出 ?
,如何判断一个提案是 ?
而不是一个确定的结果呢?
我们可以这样,如果求得的一个可能的答案为 y
,那么我们加一条从 到 的边,即强制选择 。再跑一遍 ,如果还有解,那么说明这个提案否定和通过都可以;如果无解,那么说明这个提案最后必须是通过。
cpp12345678910111213//判断是否可以为问号
for(int i=1;i<=n;i++){
if(ans[i]==true){
must(i,'n');
tarjan();
if(check()) ans[i]=3;
}else{
must(i,'y');
tarjan();
if(check()) ans[i]=3;
}
redo();
}
数组中 代表必须是否定, 代表必须是肯定, 代表都可以。
注意这里有个 函数,用来撤销用于判断一个提案是否为 ?
时建的边:
cpp1234inline void redo(){
head[edge[size].from]=edge[size].next;
size--;
}
建图部分:
cpp12345678910111213141516struct Edge{
int from,next,to;
} edge[15*500];
int head[201];
int size;
inline void add(int u,int v){
size++;
edge[size].to=v;
edge[size].next=head[u];
edge[size].from=u;
head[u]=size;
}
inline void redo(){
head[edge[size].from]=edge[size].next;
size--;
}
(注意 数组的大小。为了 操作, 结构体中新增了一个成员:)
最后根据 数组输出。
cpp123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186#include<iostream>
#include<cstring>
#include<stack>
#include<cstdio>
#define maxn 201
using namespace std;
struct Edge{
int from,next,to;
} edge[15*500];
int head[201];
int size;
inline void add(int u,int v){
size++;
edge[size].to=v;
edge[size].next=head[u];
edge[size].from=u;
head[u]=size;
}
inline void redo(){
head[edge[size].from]=edge[size].next;
size--;
}
int n;
stack<int> s;
int low[maxn],dfn[maxn];
int _time;
bool lock[maxn];
int id[maxn];
int tot;
void dfs(int x);
void tarjan(){
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
_time=0;
memset(lock,0,sizeof(lock));
memset(id,0,sizeof(id));
tot=0;
for(int i=1;i<=n*2;i++){
if(!dfn[i]) dfs(i);
}
}
void dfs(int x){
low[x]=dfn[x]=++_time;
s.push(x);
lock[x]=true;
for(int i=head[x];i;i=edge[i].next){
int to=edge[i].to;
if(!dfn[to]){
dfs(to);
low[x]=min(low[x],low[to]);
}else if(lock[to]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x]){
tot++;
while(true){
int k=s.top();s.pop();
id[k]=tot;
lock[k]=false;
if(k==x) break;
}
}
}
inline void must(int e,char a){
if(a=='y'){
add(e,e+n);
}else{
add(e+n,e);
}
}
inline void op(int e1,char a1,int e2,char a2){
if(a1=='y'){
if(a2=='y') add(e1,e2+n);
else add(e1,e2);
}else{
if(a2=='y') add(e1+n,e2+n);
else add(e1+n,e2);
}
}
int ans[101];
inline bool check(){
for(int i=1;i<=n;i++){
if(id[i]==id[i+n]){
return false;
}
}
return true;
}
inline void clear(){
memset(edge,0,sizeof(edge));
memset(head,0,sizeof(head));
size=0;
}
inline void subtask(int cas,int b,int m){
clear();
n=b;
for(int i=1;i<=m;i++){
int k,e1,e2,e3,e4;
char a1,a2,a3,a4;
cin>>k;
if(k==1){
cin>>e1>>a1;
must(e1,a1);
}
if(k==2){
cin>>e1>>a1>>e2>>a2;
must(e1,a1);
must(e2,a2);
}
if(k==3){
cin>>e1>>a1>>e2>>a2>>e3>>a3;
op(e1,a1,e2,a2);
op(e1,a1,e3,a3);
op(e2,a2,e1,a1);
op(e2,a2,e3,a3);
op(e3,a3,e2,a2);
op(e3,a3,e1,a1);
}
if(k==4){
cin>>e1>>a1>>e2>>a2>>e3>>a3>>e4>>a4;
op(e1,a1,e2,a2);
op(e1,a1,e3,a3);
op(e1,a1,e4,a4);
op(e2,a2,e1,a1);
op(e2,a2,e3,a3);
op(e2,a2,e4,a4);
op(e3,a3,e2,a2);
op(e3,a3,e1,a1);
op(e3,a3,e4,a4);
op(e4,a4,e2,a2);
op(e4,a4,e1,a1);
op(e4,a4,e3,a3);
}
}
tarjan();
if(!check()){
cout<<"Case "<<cas<<": impossible"<<endl;
return;
}
for(int i=1;i<=n;i++){
if(id[i+n]<id[i]) ans[i]=true;
else ans[i]=0;
}
//判断是否可以为问号
for(int i=1;i<=n;i++){
if(ans[i]==true){
must(i,'n');
tarjan();
if(check()) ans[i]=3;
}else{
must(i,'y');
tarjan();
if(check()) ans[i]=3;
}
redo();
}
cout<<"Case "<<cas<<": ";
for(int i=1;i<=n;i++){
if(ans[i]==0) cout<<"n";
else if(ans[i]==1) cout<<"y";
else if(ans[i]==3) cout<<"?";
}
cout<<endl;
}
int main(){
int cas=0;
while(true){
cas++;
int b,m;
cin>>b>>m;
if(b==0&&m==0) return 0;
else subtask(cas,b,m);
}
return 0;
}
UVA1086 The Ministers' Major Mess https://www.luogu.com.cn/problem/UVA1086 ↩︎