T1 神奇的幻方
按照题意模拟即可。
T2 信息传递
也是很水的一道题。有两种方法可以做。第一种是tarjan来求最小环,这个代码量略大。另外一种就是利用改版并查集,father数组存的是最远的祖先。具体实现看代码
#include<bits/stdc++.h>
using namespace std;
int f[200010],d[200010],n,minn,last;
int find(int x){
if (f[x]!=x){
int tmp=f[x];
f[x]=find(f[x]);
d[x]+=d[tmp];
}
return f[x];
}
int main(){
freopen("message.in","r",stdin);
freopen("message.out","w",stdout);
cin>>n;
for (int i=1;i<=n;i++) f[i]=i;
minn=0x7f7f7f7f;
for (int i=1;i<=n;i++) {
int t;
cin>>t;
int x=find(i),y=find(t);
if (x!=y) f[x]=y,d[i]=d[t]+1;
else minn=min(minn,d[i]+d[t]+1);
}
cout<<minn;
return 0;
}
T3 斗地主
这题挺有意思的,写完后发现会了很多斗地主技巧。首先一个显而易见的事实:有顺子时打顺子不一定是最优的,比如说我有一副 3 4 5 6 6 7 7 8 9 10,如果要打最长的顺子,这套牌要分三次才能打完。于是乎,我们只能暴力枚举了。另外其实还需要考虑其他的细节,但是这一题是随机数据,所以不加这些细节也能AC。如果要更高端的解法请移步这一题。
#include<bits/stdc++.h>
using namespace std;
int t,n,a[20],ans;
void dfs(int x){
if (x>ans) return;
int s1,s2,s3,s4;
s1=s2=s3=s4=0;
for(int i=1;i<=14;i++)//统计单牌与对子
{
if(a[i]==1) s1++;
if(a[i]==2) s2++;
}
for(int i=1;i<=14;i++)//四带的牌
if(a[i]==4)
{
s4++;
if(s1>=2) s1-=2;
else if(s2>=2) s2-=2;
else if(s2>=1) s2--;
}
for(int i=1;i<=14;i++)//三带的牌
if(a[i]==3)
{
s3++;
if(s1>=1) s1--;
else if(s2>=1) s2--;
}
ans=min(ans,x+s1+s2+s3+s4);//手中没牌,更新方案
//以下枚举顺子
int j;
for(int i=1;i<=8;i++){
for (j=i;j<=12;j++){
a[j]--;
if (a[j]<0) break;
if(j-i>=4) dfs(x+1);
}
if (j>=13) j--;
while (j>=i) a[j--]++;
}
for (int i=1;i<=10;i++){
for(j=i;i<=12;j++){
a[j]-=2;
if(a[j]<0) break;
if(j-i>=2) dfs(x+1);
}
if (j>=13) j--;
while (j>=i) a[j--]+=2;
}
for (int i=1;i<=11;i++){
for(j=i;i<=12;j++){
a[j]-=3;
if(a[j]<0) break;
if(j-i>=1) dfs(x+1);
}
if (j>=13) j--;
while (j>=i) a[j--]+=3;
}
}
int main(){
freopen("landlords.in","r",stdin);
freopen("landlords.out","w",stdout);
cin>>t>>n;
for(int i=1;i<=t;i++){
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
if (x==0) a[14]++;
if (x==1) a[12]++;
if (x==2) a[13]++;
if (x>=3) a[x-2]++;
}
ans=0x7f7f7f;
dfs(0);
cout<<ans<<endl;
}
return 0;
}
