20210524 考试总结.
T1 [LOJ6178]景区路线规划
考时操作
想都没想..这显然是一道概率啊~~~
心里想的:这道题我决定使用记忆化搜索..
手上码的:算了不管了..不好想..直接爆搜一个水点儿分过去了..
最终水了 55 分..
考后反思
显然这是一道概率与期望..
对于这道期望题,我更愿意称之为一道概率 。
目前以我的认知:
对于我们了解到初状态的题目..
我们可以选择顺推求解..
而对于我们已经知道最终结果的题目..
我们可以选择逆推求解..
无论概率与期望..并无本质区别..
(毕竟期望等于 各个值*相应的概率..)
但是你要是不知道状态怎么定义..那就GoDie了..所以人 sha 就要多做题是正确的..
(在此 %%% WTZ 大佬..)
再次回到这道题..
我们发现这道题是一道让我们求解期望的题目..
并且我们知道最初的状态即为随机选择各个点..且该概率相等..
所以我们可以选择正推..
这里是 dp 的代码..
#include<bits/stdc++.h> using namespace std; #define ll long long int #define lf double #define re register ll inline void read(ll &ss) { ss=0; ll cit=0; char ch; ch=getchar(); while(ch>'9' or ch<'0') { if(ch=='-') cit=1; ch=getchar(); } while(ch>='0' and ch<='9') { ss=(ss<<3)+(ss<<1)+(ch^48); ch=getchar(); } if(cit) ss=-ss; } ll n; ll m; ll less; ll alls; ll req[200]; ll h1[200]; ll h2[200]; lf ans1,ans2; ll xi,yi,ti; lf g[1000][1000]; ll f[1000]; ll ts; struct Attractions { ll u; ll v; ll w; ll num; ll next; }a[5000]; inline void add(ll u,ll v,ll w) { a[++ts].u=u; a[ts].v=v; a[ts].w=w; a[ts].num=ts; a[ts].next=f[u]; f[u]=ts; } inline lf max(ll aa,ll bb) { return aa>bb?aa:bb; } signed main() { read(n); read(m); read(alls); for(re i=1;i<=n;i++) { read(req[i]); read(h1[i]); read(h2[i]); g[req[i]][i]=1.0/n; } for(re i=1;i<=m;i++) { read(xi); read(yi); read(ti); add(xi,yi,ti); add(yi,xi,ti); } ll num; for(re i=0;i<=alls;i++) { for(re j=1;j<=n;j++) { num=0; for(re k=f[j];k;k=a[k].next) { if(i+a[k].w+req[a[k].v]<=alls) num++; } for(re k=f[j];k;k=a[k].next) { if(i+a[k].w+req[a[k].v]<=alls) { g[i+a[k].w+req[a[k].v]][a[k].v]+=g[i][j]/(lf)num; } } } } for(re i=1;i<=n;i++) { for(re j=1;j<=alls;j++) { ans1+=g[j][i]*h1[i]; ans2+=g[j][i]*h2[i]; } } printf("%.5lf %.5lf",ans1,ans2); return 0; }
T2 [中山市选2009]树
考时操作
嗯哼..元素之间彼此会造成影响..显然高斯消元..
那么这道题会不会产生多组解的困扰..?
算了..直接码..!!!
(打完唯一解的代码之后..)
该考虑考虑多组解了..
诶..?
既然会有自由元的产生..那么最后让求最小操作数..
不管了..直接上 dfs 往回带自由元..
(打完 dfs 的代码之后..)
诶..?
为什么会产生多组解和自由元呢..?
不管了..
把 dfs 的代码注释掉吧..
应该没有自由元..
算了不管了..交上赶紧看下一题..
最后..
1 #include<bits/stdc++.h>
2 using namespace std;
3 #define ll long long int
4 #define lf double
5 #define re register ll
6 const lf eps=1e-8;
7 inline void read(ll &ss)
8 {
9 ss=0;
10 ll cit=0;
11 char ch;
12 ch=getchar();
13 while(ch>'9' or ch<'0')
14 {
15 if(ch=='-') cit=1;
16 ch=getchar();
17 }
18 while(ch>='0' and ch<='9')
19 {
20 ss=(ss<<3)+(ss<<1)+(ch^48);
21 ch=getchar();
22 }
23 if(cit) ss=-ss;
24 }
25 // xor 或者 mod 2
26 ll n;
27 ll a[150][150];
28 ll one,two;
29 ll num;
30 ll x[150];
31 /*void dfs(ll left)
32 {
33 if(left==0)
34 {
35 ll temp;
36 ll ans=0;
37 memset(x,0,sizeof(x));
38 for(re i=n;i>=1;i--)
39 {
40 temp=a[i][n+1] & 1;
41 for(re j=n;j>=i+1;j--)
42 {
43 if(a[i][j]) temp = temp xor x[j];
44 }
45 x[i]=temp;
46 if(x[i]) ans++;
47 }
48 minn=min(minn,ans);
49 return ;
50 }
51 else
52 {
53 a[n-left+1][n-left+1]=1;
54 a[n-left+1][n+1]=1;
55 dfs(left-1);
56 a[n-left+1][n+1]=0;
57 dfs(left-1);
58 return ;
59 }
60 return ;
61 }*/
62 signed main()
63 {
64 //数据应该保证有解..
65 //但是为了输出最小开关次数,所以需要..
66 read(n);
67 while(n!=0)
68 {
69 memset(a,0,sizeof(a));
70 for(re i=1;i<=n-1;i++)
71 {
72 read(one);
73 read(two);
74 a[two][one]=1;
75 a[one][two]=1;
76 }
77 for(re i=1;i<=n;i++)
78 {
79 a[i][i]=1;
80 a[i][n+1]=1;
81 }
82 ll s;
83 for(re i=1;i<=n;i++) //枚举 i 行
84 {
85 s=0;
86 for(re j=i;j<=n+1;j++) //枚举 j 列
87 {
88 for(re k=i;k<=n;k++) // 枚举要被交换的一行
89 {
90 if(a[k][j]!=0)
91 {
92 if(k!=i)
93 {
94 for(re h=1;h<=n+1;h++)
95 {
96 swap(a[i][h],a[k][h]);
97 }
98 }
99 s=j;
100 break;
101 }
102 else continue;
103 }
104 if(s!=0) break;
105 }
106 for(re j=i+1;j<=n;j++) //开始扫荡,即枚举每一个行,使大于 i 行的第 s 列清空
107 {
108 if(a[j][s]%2==0)
109 {
110 a[j][s]=0;
111 continue;
112 }
113 for(re k=s;k<=n+1;k++)
114 {
115 a[j][k]=a[j][k] xor a[i][k];
116 }
117 }
118 }
119 /*for(re i=n;i>=1;i--)
120 {
121 ll cit=0;
122 for(re j=1;j<=n+1;j++)
123 {
124 if(a[i][j]!=0)
125 {
126 cit=1;
127 break;
128 }
129 }
130 if(cit==0) num++;
131 }*/
132 ll temp;
133 ll ans=0;
134 memset(x,0,sizeof(x));
135 for(re i=n;i>=1;i--)
136 {
137 temp=a[i][n+1] & 1;
138 for(re j=n;j>=i+1;j--)
139 {
140 if(a[i][j]) temp = temp xor x[j];
141 }
142 x[i]=temp;
143 if(x[i]) ans++;
144 }
145 // dfs(num);
146 printf("%lld\n",ans);
147 read(n);
148 }
149 return 0;
150 }
考后反思
这道题可以选择 高斯消元 或 树形动态规划..
个人认为正解应该是 高斯消元 + 深搜 ..
另外还有一道题:Lights G
这是我个人认为更加普遍的高斯消元求解异或方程组..
还应该加上一些随机化操作..
这里是 高斯消元+ dfs 的代码..
#include<bits/stdc++.h> using namespace std; #define ll long long int #define lf double #define re register ll const lf eps=1e-8; inline void read(ll &ss) { ss=0; ll cit=0; char ch; ch=getchar(); while(ch>'9' or ch<'0') { if(ch=='-') cit=1; ch=getchar(); } while(ch>='0' and ch<='9') { ss=(ss<<3)+(ss<<1)+(ch^48); ch=getchar(); } if(cit) ss=-ss; } // xor 或者 mod 2 ll n; ll a[1500][1500]; ll one,two; ll num; ll x[1500]; ll f[1500]; ll cit[1500]; ll minn=1e9; void dfs(ll left) { //cout<<left<<endl; if(left==num) { ll temp; ll ans=0; //memset(x,0,sizeof(x)); for(re i=n;i>=1;i--) { if(cit[i]) continue; temp=a[i][n+1] & 1; for(re j=i+1;j<=n;j++) { if(a[i][j]) temp = temp xor x[j]; } x[i]=temp & 1; } for(re i=1;i<=n;i++) { if(x[i]!=0) ans++; } //cout<<ans<<endl; minn=min(minn,ans); // cout<<"minn:"<<minn<<endl; return ; } else { left++; cit[f[left]]=1; x[f[left]]=1; dfs(left); x[f[left]]=0; dfs(left); return ; } return ; } signed main() { //数据应该保证有解.. //但是为了输出最小开关次数,所以需要.. read(n); while(n!=0) { memset(a,0,sizeof(a)); memset(f,0,sizeof(f)); memset(cit,0,sizeof(cit)); minn=1e9; num=0; for(re i=1;i<=n-1;i++) { read(one); read(two); a[two][one]=1; a[one][two]=1; } for(re i=1;i<=n;i++) { a[i][i]=1; a[i][n+1]=1; } ll s; for(re i=1;i<=n;i++) //枚举 i 行 { s=0; for(re k=i;k<=n;k++) // 枚举要被交换的一行 { if(a[k][i]!=0) { s=1; if(k!=i) { for(re h=1;h<=n+1;h++) { swap(a[i][h],a[k][h]); } } break; } else continue; } if(s==0) continue; for(re j=i+1;j<=n;j++) { if(a[j][i]==0) continue; for(re k=i;k<=n+1;k++) { a[j][k]=a[j][k] xor a[i][k]; } } } for(re i=1;i<=n;i++) { if(a[i][i]==0) { num++; f[num]=i; cit[i]=1; } } ll temp; dfs(0); printf("%lld\n",minn); read(n); } return 0; }
我竟然认为这是一道排列组合..?
看到这道题题面让求方案数的我:
这不显然是排列组合吗..?
k<=8..??? 算了不重要..
结果最后这道题竟然是一个依靠状压dp AC的题目..
压的就是 k ..
最后我由于时间不够只写了一部分的 code,然后剩下的就靠 ' cout<<0<<endl; ' 骗分了..
写完了估计也不对..
这道题是一道状压 dp..
看到数据在25以内应该想到状压的..
(另外..看到100左右的数据还应该高斯消元..dd..)
只要想出数组分别代表的含义..然后简单刷个表就可以 AC 了..
#include<bits/stdc++.h> using namespace std; #define ll long long int #define re register ll const ll p=1000000007; inline void read(ll &ss) { ss=0; ll cit=0; char ch; ch=getchar(); while(ch>'9' or ch<'0') { if(ch=='-') cit=1; ch=getchar(); } while(ch>='0' and ch<='9') { ss=(ss<<3)+(ss<<1)+(ch^48); ch=getchar(); } if(cit) ss=-ss; } ll n; ll m; ll limit; ll f[35][35][1<<9][10];//前 i 个点,连接了 j 条边,与 i 相邻的前 h 个点的状态为 k,前 h 个点 signed main() { read(n); read(m); read(limit); f[1][0][0][0]=1;// 初始化,对于 i 个点,连接 0 条边,显然方案为 1 for(re i=1;i<=n;i++) { for(re j=0;j<=m;j++) { for(re k=0;k<=(1<<(limit+1))-1;k++) { for(re h=min(i-1,limit);h>0;h--) // 使得'从i开始向前数个点的状态'转移到当今,即更新状态 { f[i][j][k][h-1]=(f[i][j][k][h]+f[i][j][k][h-1])%p; f[i][j+1][(k xor 1) xor (1<<h)][h]+=f[i][j][k][h];// 与之相连 f[i][j+1][(k xor 1) xor (1<<h)][h]%=p; } if((k & (1<<limit))==0) { f[i+1][j][k<<1][min(i,limit)]+=f[i][j][k][0]; f[i+1][j][k<<1][min(i,limit)]%=p; } } } } printf("%lld",f[n][m][0][0]); return 0; }
总结与反思
希望自己在以后做题时多留意一些各个类型破题的关键入口吧..
难题无非就是 困难的思路 + 巨型的码量 + 多样的专题类型结合 ..
要巩固好自己de基础代码能力..(比如dfs..递归..等等)
另外..多去思考..难题总要做过去..