0%

「JOISC 2020 Day4」传奇团子师傅

题目链接

题意

给出网格图,每格有一个团子。可以按「粉 - 白 - 绿」在八个方向串成一串团子。每个团子只能被串至多一次。最大化串出的团子串数。

思路

对网格图上每种可能串出的团子串建立点,每对冲突的团子串连边。
问题转化为求最大的点集使得任意两点没有连边。

考虑模拟退火即可,注意降温系数不能太高。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include<cstdio>
#include<vector>
#include<random>
#include<ctime>
#include<cmath>
#define For(i,l,r) for(int i=l;i<r;++i)
using namespace std;
const int N=500,D=8;
const int dx[D]={0,-1,-1,-1,0,1,1,1},dy[D]={1,1,0,-1,-1,-1,0,1};
const double T0=100,TD=0.9999999;
int n,m;
char mp[N][N];
int id[N][N][D];
int goal;
namespace random {
mt19937 rnd(time(0));
inline double rand01()
{
return (double)rnd()/0xffffffff;
}
inline int rand(int l,int r)
{
return l+rnd()%(r-l+1);
}
}
namespace graph {
int p;
vector<int>e[N*N+1];
struct plan {
bool sel[N*N+1];
int cnt;
plan()
{
cnt=0,fill(sel,sel+N*N+1,false);
return;
}
};
inline int newPoint()
{
return ++p;
}
inline void addEdge(int u,int v)
{
e[u].push_back(v),e[v].push_back(u);
return;
}
int calc(plan *pla,int u)
{
int ret=1;
for(int v:e[u]) {
if(pla->sel[v]) {
--ret;
}
}
return ret;
}
void select(plan &pla,int u)
{
pla.sel[u]=true,++pla.cnt;
for(int v:e[u]) {
if(pla.sel[v]) {
pla.sel[v]=false,--pla.cnt;
}
}
return;
}
}
inline bool checkMp(int x,int y,char c)
{
return (x>=0)&&(x<n)&&(y>=0)&&(y<m)&&(mp[x][y]==c);
}
void generateGraph()
{
For(i,0,n) {
For(j,0,m) {
if(mp[i][j]!='W') {
continue;
}
For(d,0,D) {
if(checkMp(i+dx[d],j+dy[d],'P')&&checkMp(i-dx[d],j-dy[d],'G')) {
id[i][j][d]=graph::newPoint();
}
}
}
}
For(i,0,n) {
For(j,0,m) {
switch(mp[i][j]) {
case 'W': {
For(d1,0,D-1) {
if(!id[i][j][d1]) {
continue;
}
For(d2,d1+1,D) {
if(id[i][j][d2]) {
graph::addEdge(id[i][j][d1],id[i][j][d2]);
}
}
}
break;
}
case 'P': {
For(d1,0,D-1) {
if(!checkMp(i-dx[d1],j-dy[d1],'W')||!checkMp(i-dx[d1]*2,j-dy[d1]*2,'G')) {
continue;
}
For(d2,d1+1,D) {
if(checkMp(i-dx[d2],j-dy[d2],'W')&&checkMp(i-dx[d2]*2,j-dy[d2]*2,'G')) {
graph::addEdge(id[i-dx[d1]][j-dy[d1]][d1],id[i-dx[d2]][j-dy[d2]][d2]);
}
}
}
break;
}
case 'G': {
For(d1,0,D-1) {
if(!checkMp(i+dx[d1],j+dy[d1],'W')||!checkMp(i+dx[d1]*2,j+dy[d1]*2,'P')) {
continue;
}
For(d2,d1+1,D) {
if(checkMp(i+dx[d2],j+dy[d2],'W')&&checkMp(i+dx[d2]*2,j+dy[d2]*2,'P')) {
graph::addEdge(id[i+dx[d1]][j+dy[d1]][d1],id[i+dx[d2]][j+dy[d2]][d2]);
}
}
}
break;
}
}
}
}
return;
}
double T=T0;
graph::plan res;
void simulatedAnnealing()
{
using namespace graph;
plan sol;
for(int cnt=1;sol.cnt<goal;++cnt) {
int u;
do {
u=random::rand(1,p);
} while(sol.sel[u]);
int tem=calc(&sol,u);
if((tem>=0)||(random::rand01()<=exp((double)tem/T))) {
select(sol,u);
}
T*=TD;
if(cnt%100000==0) {
fprintf(stderr,"Cnt: %d, Tem: %lf, Sol: %d\n",cnt,T,sol.cnt);
}
}
res=sol;
return;
}
struct output {
~output()
{
For(i,0,n) {
For(j,0,m) {
if(mp[i][j]=='W') {
bool flag=false;
For(d,0,D) {
if(res.sel[id[i][j][d]]) {
putchar("-/|\\-/|\\"[d]);
flag=true;
break;
}
}
if(!flag) {
putchar('W');
}
} else {
putchar(mp[i][j]);
}
}
putchar('\n');
}
return;
}
}op;
int main(int argc,char *argv[])
{
freopen(argv[1],"r",stdin);
freopen(argv[2],"w",stdout);
scanf("%d%d",&n,&m);
For(i,0,n) {
scanf("%s",mp[i]);
}
generateGraph();
sscanf(argv[3],"%d",&goal);
simulatedAnnealing();
fputs("Finish.\n",stderr);
return 0;
}